roojs-ui-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358
359         /**
360          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
361          * @param {String} string
362          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
363          * @return {Object} A literal with members
364          */
365         urlDecode : function(string, overwrite){
366             if(!string || !string.length){
367                 return {};
368             }
369             var obj = {};
370             var pairs = string.split('&');
371             var pair, name, value;
372             for(var i = 0, len = pairs.length; i < len; i++){
373                 pair = pairs[i].split('=');
374                 name = decodeURIComponent(pair[0]);
375                 value = decodeURIComponent(pair[1]);
376                 if(overwrite !== true){
377                     if(typeof obj[name] == "undefined"){
378                         obj[name] = value;
379                     }else if(typeof obj[name] == "string"){
380                         obj[name] = [obj[name]];
381                         obj[name].push(value);
382                     }else{
383                         obj[name].push(value);
384                     }
385                 }else{
386                     obj[name] = value;
387                 }
388             }
389             return obj;
390         },
391
392         /**
393          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
394          * passed array is not really an array, your function is called once with it.
395          * The supplied function is called with (Object item, Number index, Array allItems).
396          * @param {Array/NodeList/Mixed} array
397          * @param {Function} fn
398          * @param {Object} scope
399          */
400         each : function(array, fn, scope){
401             if(typeof array.length == "undefined" || typeof array == "string"){
402                 array = [array];
403             }
404             for(var i = 0, len = array.length; i < len; i++){
405                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
406             }
407         },
408
409         // deprecated
410         combine : function(){
411             var as = arguments, l = as.length, r = [];
412             for(var i = 0; i < l; i++){
413                 var a = as[i];
414                 if(a instanceof Array){
415                     r = r.concat(a);
416                 }else if(a.length !== undefined && !a.substr){
417                     r = r.concat(Array.prototype.slice.call(a, 0));
418                 }else{
419                     r.push(a);
420                 }
421             }
422             return r;
423         },
424
425         /**
426          * Escapes the passed string for use in a regular expression
427          * @param {String} str
428          * @return {String}
429          */
430         escapeRe : function(s) {
431             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
432         },
433
434         // internal
435         callback : function(cb, scope, args, delay){
436             if(typeof cb == "function"){
437                 if(delay){
438                     cb.defer(delay, scope, args || []);
439                 }else{
440                     cb.apply(scope, args || []);
441                 }
442             }
443         },
444
445         /**
446          * Return the dom node for the passed string (id), dom node, or Roo.Element
447          * @param {String/HTMLElement/Roo.Element} el
448          * @return HTMLElement
449          */
450         getDom : function(el){
451             if(!el){
452                 return null;
453             }
454             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
455         },
456
457         /**
458         * Shorthand for {@link Roo.ComponentMgr#get}
459         * @param {String} id
460         * @return Roo.Component
461         */
462         getCmp : function(id){
463             return Roo.ComponentMgr.get(id);
464         },
465          
466         num : function(v, defaultValue){
467             if(typeof v != 'number'){
468                 return defaultValue;
469             }
470             return v;
471         },
472
473         destroy : function(){
474             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
475                 var as = a[i];
476                 if(as){
477                     if(as.dom){
478                         as.removeAllListeners();
479                         as.remove();
480                         continue;
481                     }
482                     if(typeof as.purgeListeners == 'function'){
483                         as.purgeListeners();
484                     }
485                     if(typeof as.destroy == 'function'){
486                         as.destroy();
487                     }
488                 }
489             }
490         },
491
492         // inpired by a similar function in mootools library
493         /**
494          * Returns the type of object that is passed in. If the object passed in is null or undefined it
495          * return false otherwise it returns one of the following values:<ul>
496          * <li><b>string</b>: If the object passed is a string</li>
497          * <li><b>number</b>: If the object passed is a number</li>
498          * <li><b>boolean</b>: If the object passed is a boolean value</li>
499          * <li><b>function</b>: If the object passed is a function reference</li>
500          * <li><b>object</b>: If the object passed is an object</li>
501          * <li><b>array</b>: If the object passed is an array</li>
502          * <li><b>regexp</b>: If the object passed is a regular expression</li>
503          * <li><b>element</b>: If the object passed is a DOM Element</li>
504          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
505          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
506          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
507          * @param {Mixed} object
508          * @return {String}
509          */
510         type : function(o){
511             if(o === undefined || o === null){
512                 return false;
513             }
514             if(o.htmlElement){
515                 return 'element';
516             }
517             var t = typeof o;
518             if(t == 'object' && o.nodeName) {
519                 switch(o.nodeType) {
520                     case 1: return 'element';
521                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
522                 }
523             }
524             if(t == 'object' || t == 'function') {
525                 switch(o.constructor) {
526                     case Array: return 'array';
527                     case RegExp: return 'regexp';
528                 }
529                 if(typeof o.length == 'number' && typeof o.item == 'function') {
530                     return 'nodelist';
531                 }
532             }
533             return t;
534         },
535
536         /**
537          * Returns true if the passed value is null, undefined or an empty string (optional).
538          * @param {Mixed} value The value to test
539          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
540          * @return {Boolean}
541          */
542         isEmpty : function(v, allowBlank){
543             return v === null || v === undefined || (!allowBlank ? v === '' : false);
544         },
545         
546         /** @type Boolean */
547         isOpera : isOpera,
548         /** @type Boolean */
549         isSafari : isSafari,
550         /** @type Boolean */
551         isIE : isIE,
552         /** @type Boolean */
553         isIE7 : isIE7,
554         /** @type Boolean */
555         isGecko : isGecko,
556         /** @type Boolean */
557         isBorderBox : isBorderBox,
558         /** @type Boolean */
559         isWindows : isWindows,
560         /** @type Boolean */
561         isLinux : isLinux,
562         /** @type Boolean */
563         isMac : isMac,
564
565         /**
566          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
567          * you may want to set this to true.
568          * @type Boolean
569          */
570         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
571         
572         
573                 
574         /**
575          * 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 (ok-add/ok-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     {
18411         this.valid = true;
18412         this.fireEvent('enter', dd, e, data);
18413         if(this.overClass){
18414             this.el.addClass(this.overClass);
18415         }
18416         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18417             this.valid ? this.dropAllowed : this.dropNotAllowed
18418         );
18419     },
18420
18421     /**
18422      * @hide
18423      */
18424     notifyOver : function(dd, e, data)
18425     {
18426         this.valid = true;
18427         this.fireEvent('over', dd, e, data);
18428         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18429             this.valid ? this.dropAllowed : this.dropNotAllowed
18430         );
18431     },
18432
18433     /**
18434      * @hide
18435      */
18436     notifyOut : function(dd, e, data)
18437     {
18438         this.fireEvent('out', dd, e, data);
18439         if(this.overClass){
18440             this.el.removeClass(this.overClass);
18441         }
18442     },
18443
18444     /**
18445      * @hide
18446      */
18447     notifyDrop : function(dd, e, data)
18448     {
18449         this.success = false;
18450         this.fireEvent('drop', dd, e, data);
18451         return this.success;
18452     }
18453 });/*
18454  * Based on:
18455  * Ext JS Library 1.1.1
18456  * Copyright(c) 2006-2007, Ext JS, LLC.
18457  *
18458  * Originally Released Under LGPL - original licence link has changed is not relivant.
18459  *
18460  * Fork - LGPL
18461  * <script type="text/javascript">
18462  */
18463
18464
18465 /**
18466  * @class Roo.dd.DragZone
18467  * @extends Roo.dd.DragSource
18468  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18469  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18470  * @constructor
18471  * @param {String/HTMLElement/Element} el The container element
18472  * @param {Object} config
18473  */
18474 Roo.dd.DragZone = function(el, config){
18475     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18476     if(this.containerScroll){
18477         Roo.dd.ScrollManager.register(this.el);
18478     }
18479 };
18480
18481 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18482     /**
18483      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18484      * for auto scrolling during drag operations.
18485      */
18486     /**
18487      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18488      * method after a failed drop (defaults to "c3daf9" - light blue)
18489      */
18490
18491     /**
18492      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18493      * for a valid target to drag based on the mouse down. Override this method
18494      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18495      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18496      * @param {EventObject} e The mouse down event
18497      * @return {Object} The dragData
18498      */
18499     getDragData : function(e){
18500         return Roo.dd.Registry.getHandleFromEvent(e);
18501     },
18502     
18503     /**
18504      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18505      * this.dragData.ddel
18506      * @param {Number} x The x position of the click on the dragged object
18507      * @param {Number} y The y position of the click on the dragged object
18508      * @return {Boolean} true to continue the drag, false to cancel
18509      */
18510     onInitDrag : function(x, y){
18511         this.proxy.update(this.dragData.ddel.cloneNode(true));
18512         this.onStartDrag(x, y);
18513         return true;
18514     },
18515     
18516     /**
18517      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18518      */
18519     afterRepair : function(){
18520         if(Roo.enableFx){
18521             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18522         }
18523         this.dragging = false;
18524     },
18525
18526     /**
18527      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18528      * the XY of this.dragData.ddel
18529      * @param {EventObject} e The mouse up event
18530      * @return {Array} The xy location (e.g. [100, 200])
18531      */
18532     getRepairXY : function(e){
18533         return Roo.Element.fly(this.dragData.ddel).getXY();  
18534     }
18535 });/*
18536  * Based on:
18537  * Ext JS Library 1.1.1
18538  * Copyright(c) 2006-2007, Ext JS, LLC.
18539  *
18540  * Originally Released Under LGPL - original licence link has changed is not relivant.
18541  *
18542  * Fork - LGPL
18543  * <script type="text/javascript">
18544  */
18545 /**
18546  * @class Roo.dd.DropZone
18547  * @extends Roo.dd.DropTarget
18548  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18549  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18550  * @constructor
18551  * @param {String/HTMLElement/Element} el The container element
18552  * @param {Object} config
18553  */
18554 Roo.dd.DropZone = function(el, config){
18555     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18556 };
18557
18558 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18559     /**
18560      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18561      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18562      * provide your own custom lookup.
18563      * @param {Event} e The event
18564      * @return {Object} data The custom data
18565      */
18566     getTargetFromEvent : function(e){
18567         return Roo.dd.Registry.getTargetFromEvent(e);
18568     },
18569
18570     /**
18571      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18572      * that it has registered.  This method has no default implementation and should be overridden to provide
18573      * node-specific processing if necessary.
18574      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18575      * {@link #getTargetFromEvent} for this node)
18576      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18577      * @param {Event} e The event
18578      * @param {Object} data An object containing arbitrary data supplied by the drag source
18579      */
18580     onNodeEnter : function(n, dd, e, data){
18581         
18582     },
18583
18584     /**
18585      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18586      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18587      * overridden to provide the proper feedback.
18588      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18589      * {@link #getTargetFromEvent} for this node)
18590      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18591      * @param {Event} e The event
18592      * @param {Object} data An object containing arbitrary data supplied by the drag source
18593      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18594      * underlying {@link Roo.dd.StatusProxy} can be updated
18595      */
18596     onNodeOver : function(n, dd, e, data){
18597         return this.dropAllowed;
18598     },
18599
18600     /**
18601      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18602      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18603      * node-specific processing if necessary.
18604      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18605      * {@link #getTargetFromEvent} for this node)
18606      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18607      * @param {Event} e The event
18608      * @param {Object} data An object containing arbitrary data supplied by the drag source
18609      */
18610     onNodeOut : function(n, dd, e, data){
18611         
18612     },
18613
18614     /**
18615      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18616      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18617      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18618      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18619      * {@link #getTargetFromEvent} for this node)
18620      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18621      * @param {Event} e The event
18622      * @param {Object} data An object containing arbitrary data supplied by the drag source
18623      * @return {Boolean} True if the drop was valid, else false
18624      */
18625     onNodeDrop : function(n, dd, e, data){
18626         return false;
18627     },
18628
18629     /**
18630      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18631      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18632      * it should be overridden to provide the proper feedback if necessary.
18633      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18634      * @param {Event} e The event
18635      * @param {Object} data An object containing arbitrary data supplied by the drag source
18636      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18637      * underlying {@link Roo.dd.StatusProxy} can be updated
18638      */
18639     onContainerOver : function(dd, e, data){
18640         return this.dropNotAllowed;
18641     },
18642
18643     /**
18644      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18645      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18646      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18647      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18649      * @param {Event} e The event
18650      * @param {Object} data An object containing arbitrary data supplied by the drag source
18651      * @return {Boolean} True if the drop was valid, else false
18652      */
18653     onContainerDrop : function(dd, e, data){
18654         return false;
18655     },
18656
18657     /**
18658      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18659      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18660      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18661      * you should override this method and provide a custom implementation.
18662      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18663      * @param {Event} e The event
18664      * @param {Object} data An object containing arbitrary data supplied by the drag source
18665      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18666      * underlying {@link Roo.dd.StatusProxy} can be updated
18667      */
18668     notifyEnter : function(dd, e, data){
18669         return this.dropNotAllowed;
18670     },
18671
18672     /**
18673      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18674      * This method will be called on every mouse movement while the drag source is over the drop zone.
18675      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18676      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18677      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18678      * registered node, it will call {@link #onContainerOver}.
18679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18680      * @param {Event} e The event
18681      * @param {Object} data An object containing arbitrary data supplied by the drag source
18682      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18683      * underlying {@link Roo.dd.StatusProxy} can be updated
18684      */
18685     notifyOver : function(dd, e, data){
18686         var n = this.getTargetFromEvent(e);
18687         if(!n){ // not over valid drop target
18688             if(this.lastOverNode){
18689                 this.onNodeOut(this.lastOverNode, dd, e, data);
18690                 this.lastOverNode = null;
18691             }
18692             return this.onContainerOver(dd, e, data);
18693         }
18694         if(this.lastOverNode != n){
18695             if(this.lastOverNode){
18696                 this.onNodeOut(this.lastOverNode, dd, e, data);
18697             }
18698             this.onNodeEnter(n, dd, e, data);
18699             this.lastOverNode = n;
18700         }
18701         return this.onNodeOver(n, dd, e, data);
18702     },
18703
18704     /**
18705      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18706      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18707      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18711      */
18712     notifyOut : function(dd, e, data){
18713         if(this.lastOverNode){
18714             this.onNodeOut(this.lastOverNode, dd, e, data);
18715             this.lastOverNode = null;
18716         }
18717     },
18718
18719     /**
18720      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18721      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18722      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18723      * otherwise it will call {@link #onContainerDrop}.
18724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18725      * @param {Event} e The event
18726      * @param {Object} data An object containing arbitrary data supplied by the drag source
18727      * @return {Boolean} True if the drop was valid, else false
18728      */
18729     notifyDrop : function(dd, e, data){
18730         if(this.lastOverNode){
18731             this.onNodeOut(this.lastOverNode, dd, e, data);
18732             this.lastOverNode = null;
18733         }
18734         var n = this.getTargetFromEvent(e);
18735         return n ?
18736             this.onNodeDrop(n, dd, e, data) :
18737             this.onContainerDrop(dd, e, data);
18738     },
18739
18740     // private
18741     triggerCacheRefresh : function(){
18742         Roo.dd.DDM.refreshCache(this.groups);
18743     }  
18744 });/*
18745  * Based on:
18746  * Ext JS Library 1.1.1
18747  * Copyright(c) 2006-2007, Ext JS, LLC.
18748  *
18749  * Originally Released Under LGPL - original licence link has changed is not relivant.
18750  *
18751  * Fork - LGPL
18752  * <script type="text/javascript">
18753  */
18754
18755
18756 /**
18757  * @class Roo.data.SortTypes
18758  * @singleton
18759  * Defines the default sorting (casting?) comparison functions used when sorting data.
18760  */
18761 Roo.data.SortTypes = {
18762     /**
18763      * Default sort that does nothing
18764      * @param {Mixed} s The value being converted
18765      * @return {Mixed} The comparison value
18766      */
18767     none : function(s){
18768         return s;
18769     },
18770     
18771     /**
18772      * The regular expression used to strip tags
18773      * @type {RegExp}
18774      * @property
18775      */
18776     stripTagsRE : /<\/?[^>]+>/gi,
18777     
18778     /**
18779      * Strips all HTML tags to sort on text only
18780      * @param {Mixed} s The value being converted
18781      * @return {String} The comparison value
18782      */
18783     asText : function(s){
18784         return String(s).replace(this.stripTagsRE, "");
18785     },
18786     
18787     /**
18788      * Strips all HTML tags to sort on text only - Case insensitive
18789      * @param {Mixed} s The value being converted
18790      * @return {String} The comparison value
18791      */
18792     asUCText : function(s){
18793         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18794     },
18795     
18796     /**
18797      * Case insensitive string
18798      * @param {Mixed} s The value being converted
18799      * @return {String} The comparison value
18800      */
18801     asUCString : function(s) {
18802         return String(s).toUpperCase();
18803     },
18804     
18805     /**
18806      * Date sorting
18807      * @param {Mixed} s The value being converted
18808      * @return {Number} The comparison value
18809      */
18810     asDate : function(s) {
18811         if(!s){
18812             return 0;
18813         }
18814         if(s instanceof Date){
18815             return s.getTime();
18816         }
18817         return Date.parse(String(s));
18818     },
18819     
18820     /**
18821      * Float sorting
18822      * @param {Mixed} s The value being converted
18823      * @return {Float} The comparison value
18824      */
18825     asFloat : function(s) {
18826         var val = parseFloat(String(s).replace(/,/g, ""));
18827         if(isNaN(val)) val = 0;
18828         return val;
18829     },
18830     
18831     /**
18832      * Integer sorting
18833      * @param {Mixed} s The value being converted
18834      * @return {Number} The comparison value
18835      */
18836     asInt : function(s) {
18837         var val = parseInt(String(s).replace(/,/g, ""));
18838         if(isNaN(val)) val = 0;
18839         return val;
18840     }
18841 };/*
18842  * Based on:
18843  * Ext JS Library 1.1.1
18844  * Copyright(c) 2006-2007, Ext JS, LLC.
18845  *
18846  * Originally Released Under LGPL - original licence link has changed is not relivant.
18847  *
18848  * Fork - LGPL
18849  * <script type="text/javascript">
18850  */
18851
18852 /**
18853 * @class Roo.data.Record
18854  * Instances of this class encapsulate both record <em>definition</em> information, and record
18855  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18856  * to access Records cached in an {@link Roo.data.Store} object.<br>
18857  * <p>
18858  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18859  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18860  * objects.<br>
18861  * <p>
18862  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18863  * @constructor
18864  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18865  * {@link #create}. The parameters are the same.
18866  * @param {Array} data An associative Array of data values keyed by the field name.
18867  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18868  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18869  * not specified an integer id is generated.
18870  */
18871 Roo.data.Record = function(data, id){
18872     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18873     this.data = data;
18874 };
18875
18876 /**
18877  * Generate a constructor for a specific record layout.
18878  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18879  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18880  * Each field definition object may contain the following properties: <ul>
18881  * <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,
18882  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18883  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18884  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18885  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18886  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18887  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18888  * this may be omitted.</p></li>
18889  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18890  * <ul><li>auto (Default, implies no conversion)</li>
18891  * <li>string</li>
18892  * <li>int</li>
18893  * <li>float</li>
18894  * <li>boolean</li>
18895  * <li>date</li></ul></p></li>
18896  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18897  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18898  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18899  * by the Reader into an object that will be stored in the Record. It is passed the
18900  * following parameters:<ul>
18901  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18902  * </ul></p></li>
18903  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18904  * </ul>
18905  * <br>usage:<br><pre><code>
18906 var TopicRecord = Roo.data.Record.create(
18907     {name: 'title', mapping: 'topic_title'},
18908     {name: 'author', mapping: 'username'},
18909     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18910     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18911     {name: 'lastPoster', mapping: 'user2'},
18912     {name: 'excerpt', mapping: 'post_text'}
18913 );
18914
18915 var myNewRecord = new TopicRecord({
18916     title: 'Do my job please',
18917     author: 'noobie',
18918     totalPosts: 1,
18919     lastPost: new Date(),
18920     lastPoster: 'Animal',
18921     excerpt: 'No way dude!'
18922 });
18923 myStore.add(myNewRecord);
18924 </code></pre>
18925  * @method create
18926  * @static
18927  */
18928 Roo.data.Record.create = function(o){
18929     var f = function(){
18930         f.superclass.constructor.apply(this, arguments);
18931     };
18932     Roo.extend(f, Roo.data.Record);
18933     var p = f.prototype;
18934     p.fields = new Roo.util.MixedCollection(false, function(field){
18935         return field.name;
18936     });
18937     for(var i = 0, len = o.length; i < len; i++){
18938         p.fields.add(new Roo.data.Field(o[i]));
18939     }
18940     f.getField = function(name){
18941         return p.fields.get(name);  
18942     };
18943     return f;
18944 };
18945
18946 Roo.data.Record.AUTO_ID = 1000;
18947 Roo.data.Record.EDIT = 'edit';
18948 Roo.data.Record.REJECT = 'reject';
18949 Roo.data.Record.COMMIT = 'commit';
18950
18951 Roo.data.Record.prototype = {
18952     /**
18953      * Readonly flag - true if this record has been modified.
18954      * @type Boolean
18955      */
18956     dirty : false,
18957     editing : false,
18958     error: null,
18959     modified: null,
18960
18961     // private
18962     join : function(store){
18963         this.store = store;
18964     },
18965
18966     /**
18967      * Set the named field to the specified value.
18968      * @param {String} name The name of the field to set.
18969      * @param {Object} value The value to set the field to.
18970      */
18971     set : function(name, value){
18972         if(this.data[name] == value){
18973             return;
18974         }
18975         this.dirty = true;
18976         if(!this.modified){
18977             this.modified = {};
18978         }
18979         if(typeof this.modified[name] == 'undefined'){
18980             this.modified[name] = this.data[name];
18981         }
18982         this.data[name] = value;
18983         if(!this.editing){
18984             this.store.afterEdit(this);
18985         }       
18986     },
18987
18988     /**
18989      * Get the value of the named field.
18990      * @param {String} name The name of the field to get the value of.
18991      * @return {Object} The value of the field.
18992      */
18993     get : function(name){
18994         return this.data[name]; 
18995     },
18996
18997     // private
18998     beginEdit : function(){
18999         this.editing = true;
19000         this.modified = {}; 
19001     },
19002
19003     // private
19004     cancelEdit : function(){
19005         this.editing = false;
19006         delete this.modified;
19007     },
19008
19009     // private
19010     endEdit : function(){
19011         this.editing = false;
19012         if(this.dirty && this.store){
19013             this.store.afterEdit(this);
19014         }
19015     },
19016
19017     /**
19018      * Usually called by the {@link Roo.data.Store} which owns the Record.
19019      * Rejects all changes made to the Record since either creation, or the last commit operation.
19020      * Modified fields are reverted to their original values.
19021      * <p>
19022      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19023      * of reject operations.
19024      */
19025     reject : function(){
19026         var m = this.modified;
19027         for(var n in m){
19028             if(typeof m[n] != "function"){
19029                 this.data[n] = m[n];
19030             }
19031         }
19032         this.dirty = false;
19033         delete this.modified;
19034         this.editing = false;
19035         if(this.store){
19036             this.store.afterReject(this);
19037         }
19038     },
19039
19040     /**
19041      * Usually called by the {@link Roo.data.Store} which owns the Record.
19042      * Commits all changes made to the Record since either creation, or the last commit operation.
19043      * <p>
19044      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19045      * of commit operations.
19046      */
19047     commit : function(){
19048         this.dirty = false;
19049         delete this.modified;
19050         this.editing = false;
19051         if(this.store){
19052             this.store.afterCommit(this);
19053         }
19054     },
19055
19056     // private
19057     hasError : function(){
19058         return this.error != null;
19059     },
19060
19061     // private
19062     clearError : function(){
19063         this.error = null;
19064     },
19065
19066     /**
19067      * Creates a copy of this record.
19068      * @param {String} id (optional) A new record id if you don't want to use this record's id
19069      * @return {Record}
19070      */
19071     copy : function(newId) {
19072         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19073     }
19074 };/*
19075  * Based on:
19076  * Ext JS Library 1.1.1
19077  * Copyright(c) 2006-2007, Ext JS, LLC.
19078  *
19079  * Originally Released Under LGPL - original licence link has changed is not relivant.
19080  *
19081  * Fork - LGPL
19082  * <script type="text/javascript">
19083  */
19084
19085
19086
19087 /**
19088  * @class Roo.data.Store
19089  * @extends Roo.util.Observable
19090  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19091  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19092  * <p>
19093  * 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
19094  * has no knowledge of the format of the data returned by the Proxy.<br>
19095  * <p>
19096  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19097  * instances from the data object. These records are cached and made available through accessor functions.
19098  * @constructor
19099  * Creates a new Store.
19100  * @param {Object} config A config object containing the objects needed for the Store to access data,
19101  * and read the data into Records.
19102  */
19103 Roo.data.Store = function(config){
19104     this.data = new Roo.util.MixedCollection(false);
19105     this.data.getKey = function(o){
19106         return o.id;
19107     };
19108     this.baseParams = {};
19109     // private
19110     this.paramNames = {
19111         "start" : "start",
19112         "limit" : "limit",
19113         "sort" : "sort",
19114         "dir" : "dir"
19115     };
19116
19117     if(config && config.data){
19118         this.inlineData = config.data;
19119         delete config.data;
19120     }
19121
19122     Roo.apply(this, config);
19123     
19124     if(this.reader){ // reader passed
19125         this.reader = Roo.factory(this.reader, Roo.data);
19126         this.reader.xmodule = this.xmodule || false;
19127         if(!this.recordType){
19128             this.recordType = this.reader.recordType;
19129         }
19130         if(this.reader.onMetaChange){
19131             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19132         }
19133     }
19134
19135     if(this.recordType){
19136         this.fields = this.recordType.prototype.fields;
19137     }
19138     this.modified = [];
19139
19140     this.addEvents({
19141         /**
19142          * @event datachanged
19143          * Fires when the data cache has changed, and a widget which is using this Store
19144          * as a Record cache should refresh its view.
19145          * @param {Store} this
19146          */
19147         datachanged : true,
19148         /**
19149          * @event metachange
19150          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19151          * @param {Store} this
19152          * @param {Object} meta The JSON metadata
19153          */
19154         metachange : true,
19155         /**
19156          * @event add
19157          * Fires when Records have been added to the Store
19158          * @param {Store} this
19159          * @param {Roo.data.Record[]} records The array of Records added
19160          * @param {Number} index The index at which the record(s) were added
19161          */
19162         add : true,
19163         /**
19164          * @event remove
19165          * Fires when a Record has been removed from the Store
19166          * @param {Store} this
19167          * @param {Roo.data.Record} record The Record that was removed
19168          * @param {Number} index The index at which the record was removed
19169          */
19170         remove : true,
19171         /**
19172          * @event update
19173          * Fires when a Record has been updated
19174          * @param {Store} this
19175          * @param {Roo.data.Record} record The Record that was updated
19176          * @param {String} operation The update operation being performed.  Value may be one of:
19177          * <pre><code>
19178  Roo.data.Record.EDIT
19179  Roo.data.Record.REJECT
19180  Roo.data.Record.COMMIT
19181          * </code></pre>
19182          */
19183         update : true,
19184         /**
19185          * @event clear
19186          * Fires when the data cache has been cleared.
19187          * @param {Store} this
19188          */
19189         clear : true,
19190         /**
19191          * @event beforeload
19192          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19193          * the load action will be canceled.
19194          * @param {Store} this
19195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19196          */
19197         beforeload : true,
19198         /**
19199          * @event load
19200          * Fires after a new set of Records has been loaded.
19201          * @param {Store} this
19202          * @param {Roo.data.Record[]} records The Records that were loaded
19203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19204          */
19205         load : true,
19206         /**
19207          * @event loadexception
19208          * Fires if an exception occurs in the Proxy during loading.
19209          * Called with the signature of the Proxy's "loadexception" event.
19210          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19211          * 
19212          * @param {Proxy} 
19213          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19214          * @param {Object} load options 
19215          * @param {Object} jsonData from your request (normally this contains the Exception)
19216          */
19217         loadexception : true
19218     });
19219     
19220     if(this.proxy){
19221         this.proxy = Roo.factory(this.proxy, Roo.data);
19222         this.proxy.xmodule = this.xmodule || false;
19223         this.relayEvents(this.proxy,  ["loadexception"]);
19224     }
19225     this.sortToggle = {};
19226
19227     Roo.data.Store.superclass.constructor.call(this);
19228
19229     if(this.inlineData){
19230         this.loadData(this.inlineData);
19231         delete this.inlineData;
19232     }
19233 };
19234 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19235      /**
19236     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19237     * without a remote query - used by combo/forms at present.
19238     */
19239     
19240     /**
19241     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19242     */
19243     /**
19244     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19245     */
19246     /**
19247     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19248     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19249     */
19250     /**
19251     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19252     * on any HTTP request
19253     */
19254     /**
19255     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19256     */
19257     /**
19258     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19259     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19260     */
19261     remoteSort : false,
19262
19263     /**
19264     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19265      * loaded or when a record is removed. (defaults to false).
19266     */
19267     pruneModifiedRecords : false,
19268
19269     // private
19270     lastOptions : null,
19271
19272     /**
19273      * Add Records to the Store and fires the add event.
19274      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19275      */
19276     add : function(records){
19277         records = [].concat(records);
19278         for(var i = 0, len = records.length; i < len; i++){
19279             records[i].join(this);
19280         }
19281         var index = this.data.length;
19282         this.data.addAll(records);
19283         this.fireEvent("add", this, records, index);
19284     },
19285
19286     /**
19287      * Remove a Record from the Store and fires the remove event.
19288      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19289      */
19290     remove : function(record){
19291         var index = this.data.indexOf(record);
19292         this.data.removeAt(index);
19293         if(this.pruneModifiedRecords){
19294             this.modified.remove(record);
19295         }
19296         this.fireEvent("remove", this, record, index);
19297     },
19298
19299     /**
19300      * Remove all Records from the Store and fires the clear event.
19301      */
19302     removeAll : function(){
19303         this.data.clear();
19304         if(this.pruneModifiedRecords){
19305             this.modified = [];
19306         }
19307         this.fireEvent("clear", this);
19308     },
19309
19310     /**
19311      * Inserts Records to the Store at the given index and fires the add event.
19312      * @param {Number} index The start index at which to insert the passed Records.
19313      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19314      */
19315     insert : function(index, records){
19316         records = [].concat(records);
19317         for(var i = 0, len = records.length; i < len; i++){
19318             this.data.insert(index, records[i]);
19319             records[i].join(this);
19320         }
19321         this.fireEvent("add", this, records, index);
19322     },
19323
19324     /**
19325      * Get the index within the cache of the passed Record.
19326      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19327      * @return {Number} The index of the passed Record. Returns -1 if not found.
19328      */
19329     indexOf : function(record){
19330         return this.data.indexOf(record);
19331     },
19332
19333     /**
19334      * Get the index within the cache of the Record with the passed id.
19335      * @param {String} id The id of the Record to find.
19336      * @return {Number} The index of the Record. Returns -1 if not found.
19337      */
19338     indexOfId : function(id){
19339         return this.data.indexOfKey(id);
19340     },
19341
19342     /**
19343      * Get the Record with the specified id.
19344      * @param {String} id The id of the Record to find.
19345      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19346      */
19347     getById : function(id){
19348         return this.data.key(id);
19349     },
19350
19351     /**
19352      * Get the Record at the specified index.
19353      * @param {Number} index The index of the Record to find.
19354      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19355      */
19356     getAt : function(index){
19357         return this.data.itemAt(index);
19358     },
19359
19360     /**
19361      * Returns a range of Records between specified indices.
19362      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19363      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19364      * @return {Roo.data.Record[]} An array of Records
19365      */
19366     getRange : function(start, end){
19367         return this.data.getRange(start, end);
19368     },
19369
19370     // private
19371     storeOptions : function(o){
19372         o = Roo.apply({}, o);
19373         delete o.callback;
19374         delete o.scope;
19375         this.lastOptions = o;
19376     },
19377
19378     /**
19379      * Loads the Record cache from the configured Proxy using the configured Reader.
19380      * <p>
19381      * If using remote paging, then the first load call must specify the <em>start</em>
19382      * and <em>limit</em> properties in the options.params property to establish the initial
19383      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19384      * <p>
19385      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19386      * and this call will return before the new data has been loaded. Perform any post-processing
19387      * in a callback function, or in a "load" event handler.</strong>
19388      * <p>
19389      * @param {Object} options An object containing properties which control loading options:<ul>
19390      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19391      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19392      * passed the following arguments:<ul>
19393      * <li>r : Roo.data.Record[]</li>
19394      * <li>options: Options object from the load call</li>
19395      * <li>success: Boolean success indicator</li></ul></li>
19396      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19397      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19398      * </ul>
19399      */
19400     load : function(options){
19401         options = options || {};
19402         if(this.fireEvent("beforeload", this, options) !== false){
19403             this.storeOptions(options);
19404             var p = Roo.apply(options.params || {}, this.baseParams);
19405             // if meta was not loaded from remote source.. try requesting it.
19406             if (!this.reader.metaFromRemote) {
19407                 p._requestMeta = 1;
19408             }
19409             if(this.sortInfo && this.remoteSort){
19410                 var pn = this.paramNames;
19411                 p[pn["sort"]] = this.sortInfo.field;
19412                 p[pn["dir"]] = this.sortInfo.direction;
19413             }
19414             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19415         }
19416     },
19417
19418     /**
19419      * Reloads the Record cache from the configured Proxy using the configured Reader and
19420      * the options from the last load operation performed.
19421      * @param {Object} options (optional) An object containing properties which may override the options
19422      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19423      * the most recently used options are reused).
19424      */
19425     reload : function(options){
19426         this.load(Roo.applyIf(options||{}, this.lastOptions));
19427     },
19428
19429     // private
19430     // Called as a callback by the Reader during a load operation.
19431     loadRecords : function(o, options, success){
19432         if(!o || success === false){
19433             if(success !== false){
19434                 this.fireEvent("load", this, [], options);
19435             }
19436             if(options.callback){
19437                 options.callback.call(options.scope || this, [], options, false);
19438             }
19439             return;
19440         }
19441         // if data returned failure - throw an exception.
19442         if (o.success === false) {
19443             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19444             return;
19445         }
19446         var r = o.records, t = o.totalRecords || r.length;
19447         if(!options || options.add !== true){
19448             if(this.pruneModifiedRecords){
19449                 this.modified = [];
19450             }
19451             for(var i = 0, len = r.length; i < len; i++){
19452                 r[i].join(this);
19453             }
19454             if(this.snapshot){
19455                 this.data = this.snapshot;
19456                 delete this.snapshot;
19457             }
19458             this.data.clear();
19459             this.data.addAll(r);
19460             this.totalLength = t;
19461             this.applySort();
19462             this.fireEvent("datachanged", this);
19463         }else{
19464             this.totalLength = Math.max(t, this.data.length+r.length);
19465             this.add(r);
19466         }
19467         this.fireEvent("load", this, r, options);
19468         if(options.callback){
19469             options.callback.call(options.scope || this, r, options, true);
19470         }
19471     },
19472
19473     /**
19474      * Loads data from a passed data block. A Reader which understands the format of the data
19475      * must have been configured in the constructor.
19476      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19477      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19478      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19479      */
19480     loadData : function(o, append){
19481         var r = this.reader.readRecords(o);
19482         this.loadRecords(r, {add: append}, true);
19483     },
19484
19485     /**
19486      * Gets the number of cached records.
19487      * <p>
19488      * <em>If using paging, this may not be the total size of the dataset. If the data object
19489      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19490      * the data set size</em>
19491      */
19492     getCount : function(){
19493         return this.data.length || 0;
19494     },
19495
19496     /**
19497      * Gets the total number of records in the dataset as returned by the server.
19498      * <p>
19499      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19500      * the dataset size</em>
19501      */
19502     getTotalCount : function(){
19503         return this.totalLength || 0;
19504     },
19505
19506     /**
19507      * Returns the sort state of the Store as an object with two properties:
19508      * <pre><code>
19509  field {String} The name of the field by which the Records are sorted
19510  direction {String} The sort order, "ASC" or "DESC"
19511      * </code></pre>
19512      */
19513     getSortState : function(){
19514         return this.sortInfo;
19515     },
19516
19517     // private
19518     applySort : function(){
19519         if(this.sortInfo && !this.remoteSort){
19520             var s = this.sortInfo, f = s.field;
19521             var st = this.fields.get(f).sortType;
19522             var fn = function(r1, r2){
19523                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19524                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19525             };
19526             this.data.sort(s.direction, fn);
19527             if(this.snapshot && this.snapshot != this.data){
19528                 this.snapshot.sort(s.direction, fn);
19529             }
19530         }
19531     },
19532
19533     /**
19534      * Sets the default sort column and order to be used by the next load operation.
19535      * @param {String} fieldName The name of the field to sort by.
19536      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19537      */
19538     setDefaultSort : function(field, dir){
19539         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19540     },
19541
19542     /**
19543      * Sort the Records.
19544      * If remote sorting is used, the sort is performed on the server, and the cache is
19545      * reloaded. If local sorting is used, the cache is sorted internally.
19546      * @param {String} fieldName The name of the field to sort by.
19547      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19548      */
19549     sort : function(fieldName, dir){
19550         var f = this.fields.get(fieldName);
19551         if(!dir){
19552             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19553                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19554             }else{
19555                 dir = f.sortDir;
19556             }
19557         }
19558         this.sortToggle[f.name] = dir;
19559         this.sortInfo = {field: f.name, direction: dir};
19560         if(!this.remoteSort){
19561             this.applySort();
19562             this.fireEvent("datachanged", this);
19563         }else{
19564             this.load(this.lastOptions);
19565         }
19566     },
19567
19568     /**
19569      * Calls the specified function for each of the Records in the cache.
19570      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19571      * Returning <em>false</em> aborts and exits the iteration.
19572      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19573      */
19574     each : function(fn, scope){
19575         this.data.each(fn, scope);
19576     },
19577
19578     /**
19579      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19580      * (e.g., during paging).
19581      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19582      */
19583     getModifiedRecords : function(){
19584         return this.modified;
19585     },
19586
19587     // private
19588     createFilterFn : function(property, value, anyMatch){
19589         if(!value.exec){ // not a regex
19590             value = String(value);
19591             if(value.length == 0){
19592                 return false;
19593             }
19594             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19595         }
19596         return function(r){
19597             return value.test(r.data[property]);
19598         };
19599     },
19600
19601     /**
19602      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19603      * @param {String} property A field on your records
19604      * @param {Number} start The record index to start at (defaults to 0)
19605      * @param {Number} end The last record index to include (defaults to length - 1)
19606      * @return {Number} The sum
19607      */
19608     sum : function(property, start, end){
19609         var rs = this.data.items, v = 0;
19610         start = start || 0;
19611         end = (end || end === 0) ? end : rs.length-1;
19612
19613         for(var i = start; i <= end; i++){
19614             v += (rs[i].data[property] || 0);
19615         }
19616         return v;
19617     },
19618
19619     /**
19620      * Filter the records by a specified property.
19621      * @param {String} field A field on your records
19622      * @param {String/RegExp} value Either a string that the field
19623      * should start with or a RegExp to test against the field
19624      * @param {Boolean} anyMatch True to match any part not just the beginning
19625      */
19626     filter : function(property, value, anyMatch){
19627         var fn = this.createFilterFn(property, value, anyMatch);
19628         return fn ? this.filterBy(fn) : this.clearFilter();
19629     },
19630
19631     /**
19632      * Filter by a function. The specified function will be called with each
19633      * record in this data source. If the function returns true the record is included,
19634      * otherwise it is filtered.
19635      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19636      * @param {Object} scope (optional) The scope of the function (defaults to this)
19637      */
19638     filterBy : function(fn, scope){
19639         this.snapshot = this.snapshot || this.data;
19640         this.data = this.queryBy(fn, scope||this);
19641         this.fireEvent("datachanged", this);
19642     },
19643
19644     /**
19645      * Query the records by a specified property.
19646      * @param {String} field A field on your records
19647      * @param {String/RegExp} value Either a string that the field
19648      * should start with or a RegExp to test against the field
19649      * @param {Boolean} anyMatch True to match any part not just the beginning
19650      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19651      */
19652     query : function(property, value, anyMatch){
19653         var fn = this.createFilterFn(property, value, anyMatch);
19654         return fn ? this.queryBy(fn) : this.data.clone();
19655     },
19656
19657     /**
19658      * Query by a function. The specified function will be called with each
19659      * record in this data source. If the function returns true the record is included
19660      * in the results.
19661      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19662      * @param {Object} scope (optional) The scope of the function (defaults to this)
19663       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19664      **/
19665     queryBy : function(fn, scope){
19666         var data = this.snapshot || this.data;
19667         return data.filterBy(fn, scope||this);
19668     },
19669
19670     /**
19671      * Collects unique values for a particular dataIndex from this store.
19672      * @param {String} dataIndex The property to collect
19673      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19674      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19675      * @return {Array} An array of the unique values
19676      **/
19677     collect : function(dataIndex, allowNull, bypassFilter){
19678         var d = (bypassFilter === true && this.snapshot) ?
19679                 this.snapshot.items : this.data.items;
19680         var v, sv, r = [], l = {};
19681         for(var i = 0, len = d.length; i < len; i++){
19682             v = d[i].data[dataIndex];
19683             sv = String(v);
19684             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19685                 l[sv] = true;
19686                 r[r.length] = v;
19687             }
19688         }
19689         return r;
19690     },
19691
19692     /**
19693      * Revert to a view of the Record cache with no filtering applied.
19694      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19695      */
19696     clearFilter : function(suppressEvent){
19697         if(this.snapshot && this.snapshot != this.data){
19698             this.data = this.snapshot;
19699             delete this.snapshot;
19700             if(suppressEvent !== true){
19701                 this.fireEvent("datachanged", this);
19702             }
19703         }
19704     },
19705
19706     // private
19707     afterEdit : function(record){
19708         if(this.modified.indexOf(record) == -1){
19709             this.modified.push(record);
19710         }
19711         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19712     },
19713
19714     // private
19715     afterReject : function(record){
19716         this.modified.remove(record);
19717         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19718     },
19719
19720     // private
19721     afterCommit : function(record){
19722         this.modified.remove(record);
19723         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19724     },
19725
19726     /**
19727      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19728      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19729      */
19730     commitChanges : function(){
19731         var m = this.modified.slice(0);
19732         this.modified = [];
19733         for(var i = 0, len = m.length; i < len; i++){
19734             m[i].commit();
19735         }
19736     },
19737
19738     /**
19739      * Cancel outstanding changes on all changed records.
19740      */
19741     rejectChanges : function(){
19742         var m = this.modified.slice(0);
19743         this.modified = [];
19744         for(var i = 0, len = m.length; i < len; i++){
19745             m[i].reject();
19746         }
19747     },
19748
19749     onMetaChange : function(meta, rtype, o){
19750         this.recordType = rtype;
19751         this.fields = rtype.prototype.fields;
19752         delete this.snapshot;
19753         this.sortInfo = meta.sortInfo || this.sortInfo;
19754         this.modified = [];
19755         this.fireEvent('metachange', this, this.reader.meta);
19756     }
19757 });/*
19758  * Based on:
19759  * Ext JS Library 1.1.1
19760  * Copyright(c) 2006-2007, Ext JS, LLC.
19761  *
19762  * Originally Released Under LGPL - original licence link has changed is not relivant.
19763  *
19764  * Fork - LGPL
19765  * <script type="text/javascript">
19766  */
19767
19768 /**
19769  * @class Roo.data.SimpleStore
19770  * @extends Roo.data.Store
19771  * Small helper class to make creating Stores from Array data easier.
19772  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19773  * @cfg {Array} fields An array of field definition objects, or field name strings.
19774  * @cfg {Array} data The multi-dimensional array of data
19775  * @constructor
19776  * @param {Object} config
19777  */
19778 Roo.data.SimpleStore = function(config){
19779     Roo.data.SimpleStore.superclass.constructor.call(this, {
19780         isLocal : true,
19781         reader: new Roo.data.ArrayReader({
19782                 id: config.id
19783             },
19784             Roo.data.Record.create(config.fields)
19785         ),
19786         proxy : new Roo.data.MemoryProxy(config.data)
19787     });
19788     this.load();
19789 };
19790 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19791  * Based on:
19792  * Ext JS Library 1.1.1
19793  * Copyright(c) 2006-2007, Ext JS, LLC.
19794  *
19795  * Originally Released Under LGPL - original licence link has changed is not relivant.
19796  *
19797  * Fork - LGPL
19798  * <script type="text/javascript">
19799  */
19800
19801 /**
19802 /**
19803  * @extends Roo.data.Store
19804  * @class Roo.data.JsonStore
19805  * Small helper class to make creating Stores for JSON data easier. <br/>
19806 <pre><code>
19807 var store = new Roo.data.JsonStore({
19808     url: 'get-images.php',
19809     root: 'images',
19810     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19811 });
19812 </code></pre>
19813  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19814  * JsonReader and HttpProxy (unless inline data is provided).</b>
19815  * @cfg {Array} fields An array of field definition objects, or field name strings.
19816  * @constructor
19817  * @param {Object} config
19818  */
19819 Roo.data.JsonStore = function(c){
19820     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19821         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19822         reader: new Roo.data.JsonReader(c, c.fields)
19823     }));
19824 };
19825 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19826  * Based on:
19827  * Ext JS Library 1.1.1
19828  * Copyright(c) 2006-2007, Ext JS, LLC.
19829  *
19830  * Originally Released Under LGPL - original licence link has changed is not relivant.
19831  *
19832  * Fork - LGPL
19833  * <script type="text/javascript">
19834  */
19835
19836  
19837 Roo.data.Field = function(config){
19838     if(typeof config == "string"){
19839         config = {name: config};
19840     }
19841     Roo.apply(this, config);
19842     
19843     if(!this.type){
19844         this.type = "auto";
19845     }
19846     
19847     var st = Roo.data.SortTypes;
19848     // named sortTypes are supported, here we look them up
19849     if(typeof this.sortType == "string"){
19850         this.sortType = st[this.sortType];
19851     }
19852     
19853     // set default sortType for strings and dates
19854     if(!this.sortType){
19855         switch(this.type){
19856             case "string":
19857                 this.sortType = st.asUCString;
19858                 break;
19859             case "date":
19860                 this.sortType = st.asDate;
19861                 break;
19862             default:
19863                 this.sortType = st.none;
19864         }
19865     }
19866
19867     // define once
19868     var stripRe = /[\$,%]/g;
19869
19870     // prebuilt conversion function for this field, instead of
19871     // switching every time we're reading a value
19872     if(!this.convert){
19873         var cv, dateFormat = this.dateFormat;
19874         switch(this.type){
19875             case "":
19876             case "auto":
19877             case undefined:
19878                 cv = function(v){ return v; };
19879                 break;
19880             case "string":
19881                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19882                 break;
19883             case "int":
19884                 cv = function(v){
19885                     return v !== undefined && v !== null && v !== '' ?
19886                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19887                     };
19888                 break;
19889             case "float":
19890                 cv = function(v){
19891                     return v !== undefined && v !== null && v !== '' ?
19892                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19893                     };
19894                 break;
19895             case "bool":
19896             case "boolean":
19897                 cv = function(v){ return v === true || v === "true" || v == 1; };
19898                 break;
19899             case "date":
19900                 cv = function(v){
19901                     if(!v){
19902                         return '';
19903                     }
19904                     if(v instanceof Date){
19905                         return v;
19906                     }
19907                     if(dateFormat){
19908                         if(dateFormat == "timestamp"){
19909                             return new Date(v*1000);
19910                         }
19911                         return Date.parseDate(v, dateFormat);
19912                     }
19913                     var parsed = Date.parse(v);
19914                     return parsed ? new Date(parsed) : null;
19915                 };
19916              break;
19917             
19918         }
19919         this.convert = cv;
19920     }
19921 };
19922
19923 Roo.data.Field.prototype = {
19924     dateFormat: null,
19925     defaultValue: "",
19926     mapping: null,
19927     sortType : null,
19928     sortDir : "ASC"
19929 };/*
19930  * Based on:
19931  * Ext JS Library 1.1.1
19932  * Copyright(c) 2006-2007, Ext JS, LLC.
19933  *
19934  * Originally Released Under LGPL - original licence link has changed is not relivant.
19935  *
19936  * Fork - LGPL
19937  * <script type="text/javascript">
19938  */
19939  
19940 // Base class for reading structured data from a data source.  This class is intended to be
19941 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19942
19943 /**
19944  * @class Roo.data.DataReader
19945  * Base class for reading structured data from a data source.  This class is intended to be
19946  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19947  */
19948
19949 Roo.data.DataReader = function(meta, recordType){
19950     
19951     this.meta = meta;
19952     
19953     this.recordType = recordType instanceof Array ? 
19954         Roo.data.Record.create(recordType) : recordType;
19955 };
19956
19957 Roo.data.DataReader.prototype = {
19958      /**
19959      * Create an empty record
19960      * @param {Object} data (optional) - overlay some values
19961      * @return {Roo.data.Record} record created.
19962      */
19963     newRow :  function(d) {
19964         var da =  {};
19965         this.recordType.prototype.fields.each(function(c) {
19966             switch( c.type) {
19967                 case 'int' : da[c.name] = 0; break;
19968                 case 'date' : da[c.name] = new Date(); break;
19969                 case 'float' : da[c.name] = 0.0; break;
19970                 case 'boolean' : da[c.name] = false; break;
19971                 default : da[c.name] = ""; break;
19972             }
19973             
19974         });
19975         return new this.recordType(Roo.apply(da, d));
19976     }
19977     
19978 };/*
19979  * Based on:
19980  * Ext JS Library 1.1.1
19981  * Copyright(c) 2006-2007, Ext JS, LLC.
19982  *
19983  * Originally Released Under LGPL - original licence link has changed is not relivant.
19984  *
19985  * Fork - LGPL
19986  * <script type="text/javascript">
19987  */
19988
19989 /**
19990  * @class Roo.data.DataProxy
19991  * @extends Roo.data.Observable
19992  * This class is an abstract base class for implementations which provide retrieval of
19993  * unformatted data objects.<br>
19994  * <p>
19995  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19996  * (of the appropriate type which knows how to parse the data object) to provide a block of
19997  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19998  * <p>
19999  * Custom implementations must implement the load method as described in
20000  * {@link Roo.data.HttpProxy#load}.
20001  */
20002 Roo.data.DataProxy = function(){
20003     this.addEvents({
20004         /**
20005          * @event beforeload
20006          * Fires before a network request is made to retrieve a data object.
20007          * @param {Object} This DataProxy object.
20008          * @param {Object} params The params parameter to the load function.
20009          */
20010         beforeload : true,
20011         /**
20012          * @event load
20013          * Fires before the load method's callback is called.
20014          * @param {Object} This DataProxy object.
20015          * @param {Object} o The data object.
20016          * @param {Object} arg The callback argument object passed to the load function.
20017          */
20018         load : true,
20019         /**
20020          * @event loadexception
20021          * Fires if an Exception occurs during data retrieval.
20022          * @param {Object} This DataProxy object.
20023          * @param {Object} o The data object.
20024          * @param {Object} arg The callback argument object passed to the load function.
20025          * @param {Object} e The Exception.
20026          */
20027         loadexception : true
20028     });
20029     Roo.data.DataProxy.superclass.constructor.call(this);
20030 };
20031
20032 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20033
20034     /**
20035      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20036      */
20037 /*
20038  * Based on:
20039  * Ext JS Library 1.1.1
20040  * Copyright(c) 2006-2007, Ext JS, LLC.
20041  *
20042  * Originally Released Under LGPL - original licence link has changed is not relivant.
20043  *
20044  * Fork - LGPL
20045  * <script type="text/javascript">
20046  */
20047 /**
20048  * @class Roo.data.MemoryProxy
20049  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20050  * to the Reader when its load method is called.
20051  * @constructor
20052  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20053  */
20054 Roo.data.MemoryProxy = function(data){
20055     if (data.data) {
20056         data = data.data;
20057     }
20058     Roo.data.MemoryProxy.superclass.constructor.call(this);
20059     this.data = data;
20060 };
20061
20062 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20063     /**
20064      * Load data from the requested source (in this case an in-memory
20065      * data object passed to the constructor), read the data object into
20066      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20067      * process that block using the passed callback.
20068      * @param {Object} params This parameter is not used by the MemoryProxy class.
20069      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20070      * object into a block of Roo.data.Records.
20071      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20072      * The function must be passed <ul>
20073      * <li>The Record block object</li>
20074      * <li>The "arg" argument from the load function</li>
20075      * <li>A boolean success indicator</li>
20076      * </ul>
20077      * @param {Object} scope The scope in which to call the callback
20078      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20079      */
20080     load : function(params, reader, callback, scope, arg){
20081         params = params || {};
20082         var result;
20083         try {
20084             result = reader.readRecords(this.data);
20085         }catch(e){
20086             this.fireEvent("loadexception", this, arg, null, e);
20087             callback.call(scope, null, arg, false);
20088             return;
20089         }
20090         callback.call(scope, result, arg, true);
20091     },
20092     
20093     // private
20094     update : function(params, records){
20095         
20096     }
20097 });/*
20098  * Based on:
20099  * Ext JS Library 1.1.1
20100  * Copyright(c) 2006-2007, Ext JS, LLC.
20101  *
20102  * Originally Released Under LGPL - original licence link has changed is not relivant.
20103  *
20104  * Fork - LGPL
20105  * <script type="text/javascript">
20106  */
20107 /**
20108  * @class Roo.data.HttpProxy
20109  * @extends Roo.data.DataProxy
20110  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20111  * configured to reference a certain URL.<br><br>
20112  * <p>
20113  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20114  * from which the running page was served.<br><br>
20115  * <p>
20116  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20117  * <p>
20118  * Be aware that to enable the browser to parse an XML document, the server must set
20119  * the Content-Type header in the HTTP response to "text/xml".
20120  * @constructor
20121  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20122  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20123  * will be used to make the request.
20124  */
20125 Roo.data.HttpProxy = function(conn){
20126     Roo.data.HttpProxy.superclass.constructor.call(this);
20127     // is conn a conn config or a real conn?
20128     this.conn = conn;
20129     this.useAjax = !conn || !conn.events;
20130   
20131 };
20132
20133 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20134     // thse are take from connection...
20135     
20136     /**
20137      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20138      */
20139     /**
20140      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20141      * extra parameters to each request made by this object. (defaults to undefined)
20142      */
20143     /**
20144      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20145      *  to each request made by this object. (defaults to undefined)
20146      */
20147     /**
20148      * @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)
20149      */
20150     /**
20151      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20152      */
20153      /**
20154      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20155      * @type Boolean
20156      */
20157   
20158
20159     /**
20160      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20161      * @type Boolean
20162      */
20163     /**
20164      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20165      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20166      * a finer-grained basis than the DataProxy events.
20167      */
20168     getConnection : function(){
20169         return this.useAjax ? Roo.Ajax : this.conn;
20170     },
20171
20172     /**
20173      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20174      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20175      * process that block using the passed callback.
20176      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20177      * for the request to the remote server.
20178      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20179      * object into a block of Roo.data.Records.
20180      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20181      * The function must be passed <ul>
20182      * <li>The Record block object</li>
20183      * <li>The "arg" argument from the load function</li>
20184      * <li>A boolean success indicator</li>
20185      * </ul>
20186      * @param {Object} scope The scope in which to call the callback
20187      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20188      */
20189     load : function(params, reader, callback, scope, arg){
20190         if(this.fireEvent("beforeload", this, params) !== false){
20191             var  o = {
20192                 params : params || {},
20193                 request: {
20194                     callback : callback,
20195                     scope : scope,
20196                     arg : arg
20197                 },
20198                 reader: reader,
20199                 callback : this.loadResponse,
20200                 scope: this
20201             };
20202             if(this.useAjax){
20203                 Roo.applyIf(o, this.conn);
20204                 if(this.activeRequest){
20205                     Roo.Ajax.abort(this.activeRequest);
20206                 }
20207                 this.activeRequest = Roo.Ajax.request(o);
20208             }else{
20209                 this.conn.request(o);
20210             }
20211         }else{
20212             callback.call(scope||this, null, arg, false);
20213         }
20214     },
20215
20216     // private
20217     loadResponse : function(o, success, response){
20218         delete this.activeRequest;
20219         if(!success){
20220             this.fireEvent("loadexception", this, o, response);
20221             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20222             return;
20223         }
20224         var result;
20225         try {
20226             result = o.reader.read(response);
20227         }catch(e){
20228             this.fireEvent("loadexception", this, o, response, e);
20229             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20230             return;
20231         }
20232         
20233         this.fireEvent("load", this, o, o.request.arg);
20234         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20235     },
20236
20237     // private
20238     update : function(dataSet){
20239
20240     },
20241
20242     // private
20243     updateResponse : function(dataSet){
20244
20245     }
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256
20257 /**
20258  * @class Roo.data.ScriptTagProxy
20259  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20260  * other than the originating domain of the running page.<br><br>
20261  * <p>
20262  * <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
20263  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20264  * <p>
20265  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20266  * source code that is used as the source inside a &lt;script> tag.<br><br>
20267  * <p>
20268  * In order for the browser to process the returned data, the server must wrap the data object
20269  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20270  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20271  * depending on whether the callback name was passed:
20272  * <p>
20273  * <pre><code>
20274 boolean scriptTag = false;
20275 String cb = request.getParameter("callback");
20276 if (cb != null) {
20277     scriptTag = true;
20278     response.setContentType("text/javascript");
20279 } else {
20280     response.setContentType("application/x-json");
20281 }
20282 Writer out = response.getWriter();
20283 if (scriptTag) {
20284     out.write(cb + "(");
20285 }
20286 out.print(dataBlock.toJsonString());
20287 if (scriptTag) {
20288     out.write(");");
20289 }
20290 </pre></code>
20291  *
20292  * @constructor
20293  * @param {Object} config A configuration object.
20294  */
20295 Roo.data.ScriptTagProxy = function(config){
20296     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20297     Roo.apply(this, config);
20298     this.head = document.getElementsByTagName("head")[0];
20299 };
20300
20301 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20302
20303 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20304     /**
20305      * @cfg {String} url The URL from which to request the data object.
20306      */
20307     /**
20308      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20309      */
20310     timeout : 30000,
20311     /**
20312      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20313      * the server the name of the callback function set up by the load call to process the returned data object.
20314      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20315      * javascript output which calls this named function passing the data object as its only parameter.
20316      */
20317     callbackParam : "callback",
20318     /**
20319      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20320      * name to the request.
20321      */
20322     nocache : true,
20323
20324     /**
20325      * Load data from the configured URL, read the data object into
20326      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20327      * process that block using the passed callback.
20328      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20329      * for the request to the remote server.
20330      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20331      * object into a block of Roo.data.Records.
20332      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20333      * The function must be passed <ul>
20334      * <li>The Record block object</li>
20335      * <li>The "arg" argument from the load function</li>
20336      * <li>A boolean success indicator</li>
20337      * </ul>
20338      * @param {Object} scope The scope in which to call the callback
20339      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20340      */
20341     load : function(params, reader, callback, scope, arg){
20342         if(this.fireEvent("beforeload", this, params) !== false){
20343
20344             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20345
20346             var url = this.url;
20347             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20348             if(this.nocache){
20349                 url += "&_dc=" + (new Date().getTime());
20350             }
20351             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20352             var trans = {
20353                 id : transId,
20354                 cb : "stcCallback"+transId,
20355                 scriptId : "stcScript"+transId,
20356                 params : params,
20357                 arg : arg,
20358                 url : url,
20359                 callback : callback,
20360                 scope : scope,
20361                 reader : reader
20362             };
20363             var conn = this;
20364
20365             window[trans.cb] = function(o){
20366                 conn.handleResponse(o, trans);
20367             };
20368
20369             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20370
20371             if(this.autoAbort !== false){
20372                 this.abort();
20373             }
20374
20375             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20376
20377             var script = document.createElement("script");
20378             script.setAttribute("src", url);
20379             script.setAttribute("type", "text/javascript");
20380             script.setAttribute("id", trans.scriptId);
20381             this.head.appendChild(script);
20382
20383             this.trans = trans;
20384         }else{
20385             callback.call(scope||this, null, arg, false);
20386         }
20387     },
20388
20389     // private
20390     isLoading : function(){
20391         return this.trans ? true : false;
20392     },
20393
20394     /**
20395      * Abort the current server request.
20396      */
20397     abort : function(){
20398         if(this.isLoading()){
20399             this.destroyTrans(this.trans);
20400         }
20401     },
20402
20403     // private
20404     destroyTrans : function(trans, isLoaded){
20405         this.head.removeChild(document.getElementById(trans.scriptId));
20406         clearTimeout(trans.timeoutId);
20407         if(isLoaded){
20408             window[trans.cb] = undefined;
20409             try{
20410                 delete window[trans.cb];
20411             }catch(e){}
20412         }else{
20413             // if hasn't been loaded, wait for load to remove it to prevent script error
20414             window[trans.cb] = function(){
20415                 window[trans.cb] = undefined;
20416                 try{
20417                     delete window[trans.cb];
20418                 }catch(e){}
20419             };
20420         }
20421     },
20422
20423     // private
20424     handleResponse : function(o, trans){
20425         this.trans = false;
20426         this.destroyTrans(trans, true);
20427         var result;
20428         try {
20429             result = trans.reader.readRecords(o);
20430         }catch(e){
20431             this.fireEvent("loadexception", this, o, trans.arg, e);
20432             trans.callback.call(trans.scope||window, null, trans.arg, false);
20433             return;
20434         }
20435         this.fireEvent("load", this, o, trans.arg);
20436         trans.callback.call(trans.scope||window, result, trans.arg, true);
20437     },
20438
20439     // private
20440     handleFailure : function(trans){
20441         this.trans = false;
20442         this.destroyTrans(trans, false);
20443         this.fireEvent("loadexception", this, null, trans.arg);
20444         trans.callback.call(trans.scope||window, null, trans.arg, false);
20445     }
20446 });/*
20447  * Based on:
20448  * Ext JS Library 1.1.1
20449  * Copyright(c) 2006-2007, Ext JS, LLC.
20450  *
20451  * Originally Released Under LGPL - original licence link has changed is not relivant.
20452  *
20453  * Fork - LGPL
20454  * <script type="text/javascript">
20455  */
20456
20457 /**
20458  * @class Roo.data.JsonReader
20459  * @extends Roo.data.DataReader
20460  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20461  * based on mappings in a provided Roo.data.Record constructor.
20462  * 
20463  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20464  * in the reply previously. 
20465  * 
20466  * <p>
20467  * Example code:
20468  * <pre><code>
20469 var RecordDef = Roo.data.Record.create([
20470     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20471     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20472 ]);
20473 var myReader = new Roo.data.JsonReader({
20474     totalProperty: "results",    // The property which contains the total dataset size (optional)
20475     root: "rows",                // The property which contains an Array of row objects
20476     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20477 }, RecordDef);
20478 </code></pre>
20479  * <p>
20480  * This would consume a JSON file like this:
20481  * <pre><code>
20482 { 'results': 2, 'rows': [
20483     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20484     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20485 }
20486 </code></pre>
20487  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20488  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20489  * paged from the remote server.
20490  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20491  * @cfg {String} root name of the property which contains the Array of row objects.
20492  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20493  * @constructor
20494  * Create a new JsonReader
20495  * @param {Object} meta Metadata configuration options
20496  * @param {Object} recordType Either an Array of field definition objects,
20497  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20498  */
20499 Roo.data.JsonReader = function(meta, recordType){
20500     
20501     meta = meta || {};
20502     // set some defaults:
20503     Roo.applyIf(meta, {
20504         totalProperty: 'total',
20505         successProperty : 'success',
20506         root : 'data',
20507         id : 'id'
20508     });
20509     
20510     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20511 };
20512 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20513     
20514     /**
20515      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20516      * Used by Store query builder to append _requestMeta to params.
20517      * 
20518      */
20519     metaFromRemote : false,
20520     /**
20521      * This method is only used by a DataProxy which has retrieved data from a remote server.
20522      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20523      * @return {Object} data A data block which is used by an Roo.data.Store object as
20524      * a cache of Roo.data.Records.
20525      */
20526     read : function(response){
20527         var json = response.responseText;
20528        
20529         var o = /* eval:var:o */ eval("("+json+")");
20530         if(!o) {
20531             throw {message: "JsonReader.read: Json object not found"};
20532         }
20533         
20534         if(o.metaData){
20535             
20536             delete this.ef;
20537             this.metaFromRemote = true;
20538             this.meta = o.metaData;
20539             this.recordType = Roo.data.Record.create(o.metaData.fields);
20540             this.onMetaChange(this.meta, this.recordType, o);
20541         }
20542         return this.readRecords(o);
20543     },
20544
20545     // private function a store will implement
20546     onMetaChange : function(meta, recordType, o){
20547
20548     },
20549
20550     /**
20551          * @ignore
20552          */
20553     simpleAccess: function(obj, subsc) {
20554         return obj[subsc];
20555     },
20556
20557         /**
20558          * @ignore
20559          */
20560     getJsonAccessor: function(){
20561         var re = /[\[\.]/;
20562         return function(expr) {
20563             try {
20564                 return(re.test(expr))
20565                     ? new Function("obj", "return obj." + expr)
20566                     : function(obj){
20567                         return obj[expr];
20568                     };
20569             } catch(e){}
20570             return Roo.emptyFn;
20571         };
20572     }(),
20573
20574     /**
20575      * Create a data block containing Roo.data.Records from an XML document.
20576      * @param {Object} o An object which contains an Array of row objects in the property specified
20577      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20578      * which contains the total size of the dataset.
20579      * @return {Object} data A data block which is used by an Roo.data.Store object as
20580      * a cache of Roo.data.Records.
20581      */
20582     readRecords : function(o){
20583         /**
20584          * After any data loads, the raw JSON data is available for further custom processing.
20585          * @type Object
20586          */
20587         this.jsonData = o;
20588         var s = this.meta, Record = this.recordType,
20589             f = Record.prototype.fields, fi = f.items, fl = f.length;
20590
20591 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20592         if (!this.ef) {
20593             if(s.totalProperty) {
20594                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20595                 }
20596                 if(s.successProperty) {
20597                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20598                 }
20599                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20600                 if (s.id) {
20601                         var g = this.getJsonAccessor(s.id);
20602                         this.getId = function(rec) {
20603                                 var r = g(rec);
20604                                 return (r === undefined || r === "") ? null : r;
20605                         };
20606                 } else {
20607                         this.getId = function(){return null;};
20608                 }
20609             this.ef = [];
20610             for(var jj = 0; jj < fl; jj++){
20611                 f = fi[jj];
20612                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20613                 this.ef[jj] = this.getJsonAccessor(map);
20614             }
20615         }
20616
20617         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20618         if(s.totalProperty){
20619             var vt = parseInt(this.getTotal(o), 10);
20620             if(!isNaN(vt)){
20621                 totalRecords = vt;
20622             }
20623         }
20624         if(s.successProperty){
20625             var vs = this.getSuccess(o);
20626             if(vs === false || vs === 'false'){
20627                 success = false;
20628             }
20629         }
20630         var records = [];
20631             for(var i = 0; i < c; i++){
20632                     var n = root[i];
20633                 var values = {};
20634                 var id = this.getId(n);
20635                 for(var j = 0; j < fl; j++){
20636                     f = fi[j];
20637                 var v = this.ef[j](n);
20638                 if (!f.convert) {
20639                     Roo.log('missing convert for ' + f.name);
20640                     Roo.log(f);
20641                     continue;
20642                 }
20643                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20644                 }
20645                 var record = new Record(values, id);
20646                 record.json = n;
20647                 records[i] = record;
20648             }
20649             return {
20650                 success : success,
20651                 records : records,
20652                 totalRecords : totalRecords
20653             };
20654     }
20655 });/*
20656  * Based on:
20657  * Ext JS Library 1.1.1
20658  * Copyright(c) 2006-2007, Ext JS, LLC.
20659  *
20660  * Originally Released Under LGPL - original licence link has changed is not relivant.
20661  *
20662  * Fork - LGPL
20663  * <script type="text/javascript">
20664  */
20665
20666 /**
20667  * @class Roo.data.XmlReader
20668  * @extends Roo.data.DataReader
20669  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20670  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20671  * <p>
20672  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20673  * header in the HTTP response must be set to "text/xml".</em>
20674  * <p>
20675  * Example code:
20676  * <pre><code>
20677 var RecordDef = Roo.data.Record.create([
20678    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20679    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20680 ]);
20681 var myReader = new Roo.data.XmlReader({
20682    totalRecords: "results", // The element which contains the total dataset size (optional)
20683    record: "row",           // The repeated element which contains row information
20684    id: "id"                 // The element within the row that provides an ID for the record (optional)
20685 }, RecordDef);
20686 </code></pre>
20687  * <p>
20688  * This would consume an XML file like this:
20689  * <pre><code>
20690 &lt;?xml?>
20691 &lt;dataset>
20692  &lt;results>2&lt;/results>
20693  &lt;row>
20694    &lt;id>1&lt;/id>
20695    &lt;name>Bill&lt;/name>
20696    &lt;occupation>Gardener&lt;/occupation>
20697  &lt;/row>
20698  &lt;row>
20699    &lt;id>2&lt;/id>
20700    &lt;name>Ben&lt;/name>
20701    &lt;occupation>Horticulturalist&lt;/occupation>
20702  &lt;/row>
20703 &lt;/dataset>
20704 </code></pre>
20705  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20706  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20707  * paged from the remote server.
20708  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20709  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20710  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20711  * a record identifier value.
20712  * @constructor
20713  * Create a new XmlReader
20714  * @param {Object} meta Metadata configuration options
20715  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20716  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20717  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20718  */
20719 Roo.data.XmlReader = function(meta, recordType){
20720     meta = meta || {};
20721     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20722 };
20723 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20724     /**
20725      * This method is only used by a DataProxy which has retrieved data from a remote server.
20726          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20727          * to contain a method called 'responseXML' that returns an XML document object.
20728      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20729      * a cache of Roo.data.Records.
20730      */
20731     read : function(response){
20732         var doc = response.responseXML;
20733         if(!doc) {
20734             throw {message: "XmlReader.read: XML Document not available"};
20735         }
20736         return this.readRecords(doc);
20737     },
20738
20739     /**
20740      * Create a data block containing Roo.data.Records from an XML document.
20741          * @param {Object} doc A parsed XML document.
20742      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20743      * a cache of Roo.data.Records.
20744      */
20745     readRecords : function(doc){
20746         /**
20747          * After any data loads/reads, the raw XML Document is available for further custom processing.
20748          * @type XMLDocument
20749          */
20750         this.xmlData = doc;
20751         var root = doc.documentElement || doc;
20752         var q = Roo.DomQuery;
20753         var recordType = this.recordType, fields = recordType.prototype.fields;
20754         var sid = this.meta.id;
20755         var totalRecords = 0, success = true;
20756         if(this.meta.totalRecords){
20757             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20758         }
20759         
20760         if(this.meta.success){
20761             var sv = q.selectValue(this.meta.success, root, true);
20762             success = sv !== false && sv !== 'false';
20763         }
20764         var records = [];
20765         var ns = q.select(this.meta.record, root);
20766         for(var i = 0, len = ns.length; i < len; i++) {
20767                 var n = ns[i];
20768                 var values = {};
20769                 var id = sid ? q.selectValue(sid, n) : undefined;
20770                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20771                     var f = fields.items[j];
20772                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20773                     v = f.convert(v);
20774                     values[f.name] = v;
20775                 }
20776                 var record = new recordType(values, id);
20777                 record.node = n;
20778                 records[records.length] = record;
20779             }
20780
20781             return {
20782                 success : success,
20783                 records : records,
20784                 totalRecords : totalRecords || records.length
20785             };
20786     }
20787 });/*
20788  * Based on:
20789  * Ext JS Library 1.1.1
20790  * Copyright(c) 2006-2007, Ext JS, LLC.
20791  *
20792  * Originally Released Under LGPL - original licence link has changed is not relivant.
20793  *
20794  * Fork - LGPL
20795  * <script type="text/javascript">
20796  */
20797
20798 /**
20799  * @class Roo.data.ArrayReader
20800  * @extends Roo.data.DataReader
20801  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20802  * Each element of that Array represents a row of data fields. The
20803  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20804  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20805  * <p>
20806  * Example code:.
20807  * <pre><code>
20808 var RecordDef = Roo.data.Record.create([
20809     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20810     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20811 ]);
20812 var myReader = new Roo.data.ArrayReader({
20813     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20814 }, RecordDef);
20815 </code></pre>
20816  * <p>
20817  * This would consume an Array like this:
20818  * <pre><code>
20819 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20820   </code></pre>
20821  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20822  * @constructor
20823  * Create a new JsonReader
20824  * @param {Object} meta Metadata configuration options.
20825  * @param {Object} recordType Either an Array of field definition objects
20826  * as specified to {@link Roo.data.Record#create},
20827  * or an {@link Roo.data.Record} object
20828  * created using {@link Roo.data.Record#create}.
20829  */
20830 Roo.data.ArrayReader = function(meta, recordType){
20831     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20832 };
20833
20834 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20835     /**
20836      * Create a data block containing Roo.data.Records from an XML document.
20837      * @param {Object} o An Array of row objects which represents the dataset.
20838      * @return {Object} data A data block which is used by an Roo.data.Store object as
20839      * a cache of Roo.data.Records.
20840      */
20841     readRecords : function(o){
20842         var sid = this.meta ? this.meta.id : null;
20843         var recordType = this.recordType, fields = recordType.prototype.fields;
20844         var records = [];
20845         var root = o;
20846             for(var i = 0; i < root.length; i++){
20847                     var n = root[i];
20848                 var values = {};
20849                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20850                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20851                 var f = fields.items[j];
20852                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20853                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20854                 v = f.convert(v);
20855                 values[f.name] = v;
20856             }
20857                 var record = new recordType(values, id);
20858                 record.json = n;
20859                 records[records.length] = record;
20860             }
20861             return {
20862                 records : records,
20863                 totalRecords : records.length
20864             };
20865     }
20866 });/*
20867  * Based on:
20868  * Ext JS Library 1.1.1
20869  * Copyright(c) 2006-2007, Ext JS, LLC.
20870  *
20871  * Originally Released Under LGPL - original licence link has changed is not relivant.
20872  *
20873  * Fork - LGPL
20874  * <script type="text/javascript">
20875  */
20876
20877
20878 /**
20879  * @class Roo.data.Tree
20880  * @extends Roo.util.Observable
20881  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20882  * in the tree have most standard DOM functionality.
20883  * @constructor
20884  * @param {Node} root (optional) The root node
20885  */
20886 Roo.data.Tree = function(root){
20887    this.nodeHash = {};
20888    /**
20889     * The root node for this tree
20890     * @type Node
20891     */
20892    this.root = null;
20893    if(root){
20894        this.setRootNode(root);
20895    }
20896    this.addEvents({
20897        /**
20898         * @event append
20899         * Fires when a new child node is appended to a node in this tree.
20900         * @param {Tree} tree The owner tree
20901         * @param {Node} parent The parent node
20902         * @param {Node} node The newly appended node
20903         * @param {Number} index The index of the newly appended node
20904         */
20905        "append" : true,
20906        /**
20907         * @event remove
20908         * Fires when a child node is removed from a node in this tree.
20909         * @param {Tree} tree The owner tree
20910         * @param {Node} parent The parent node
20911         * @param {Node} node The child node removed
20912         */
20913        "remove" : true,
20914        /**
20915         * @event move
20916         * Fires when a node is moved to a new location in the tree
20917         * @param {Tree} tree The owner tree
20918         * @param {Node} node The node moved
20919         * @param {Node} oldParent The old parent of this node
20920         * @param {Node} newParent The new parent of this node
20921         * @param {Number} index The index it was moved to
20922         */
20923        "move" : true,
20924        /**
20925         * @event insert
20926         * Fires when a new child node is inserted in a node in this tree.
20927         * @param {Tree} tree The owner tree
20928         * @param {Node} parent The parent node
20929         * @param {Node} node The child node inserted
20930         * @param {Node} refNode The child node the node was inserted before
20931         */
20932        "insert" : true,
20933        /**
20934         * @event beforeappend
20935         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20936         * @param {Tree} tree The owner tree
20937         * @param {Node} parent The parent node
20938         * @param {Node} node The child node to be appended
20939         */
20940        "beforeappend" : true,
20941        /**
20942         * @event beforeremove
20943         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20944         * @param {Tree} tree The owner tree
20945         * @param {Node} parent The parent node
20946         * @param {Node} node The child node to be removed
20947         */
20948        "beforeremove" : true,
20949        /**
20950         * @event beforemove
20951         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20952         * @param {Tree} tree The owner tree
20953         * @param {Node} node The node being moved
20954         * @param {Node} oldParent The parent of the node
20955         * @param {Node} newParent The new parent the node is moving to
20956         * @param {Number} index The index it is being moved to
20957         */
20958        "beforemove" : true,
20959        /**
20960         * @event beforeinsert
20961         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20962         * @param {Tree} tree The owner tree
20963         * @param {Node} parent The parent node
20964         * @param {Node} node The child node to be inserted
20965         * @param {Node} refNode The child node the node is being inserted before
20966         */
20967        "beforeinsert" : true
20968    });
20969
20970     Roo.data.Tree.superclass.constructor.call(this);
20971 };
20972
20973 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20974     pathSeparator: "/",
20975
20976     proxyNodeEvent : function(){
20977         return this.fireEvent.apply(this, arguments);
20978     },
20979
20980     /**
20981      * Returns the root node for this tree.
20982      * @return {Node}
20983      */
20984     getRootNode : function(){
20985         return this.root;
20986     },
20987
20988     /**
20989      * Sets the root node for this tree.
20990      * @param {Node} node
20991      * @return {Node}
20992      */
20993     setRootNode : function(node){
20994         this.root = node;
20995         node.ownerTree = this;
20996         node.isRoot = true;
20997         this.registerNode(node);
20998         return node;
20999     },
21000
21001     /**
21002      * Gets a node in this tree by its id.
21003      * @param {String} id
21004      * @return {Node}
21005      */
21006     getNodeById : function(id){
21007         return this.nodeHash[id];
21008     },
21009
21010     registerNode : function(node){
21011         this.nodeHash[node.id] = node;
21012     },
21013
21014     unregisterNode : function(node){
21015         delete this.nodeHash[node.id];
21016     },
21017
21018     toString : function(){
21019         return "[Tree"+(this.id?" "+this.id:"")+"]";
21020     }
21021 });
21022
21023 /**
21024  * @class Roo.data.Node
21025  * @extends Roo.util.Observable
21026  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21027  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21028  * @constructor
21029  * @param {Object} attributes The attributes/config for the node
21030  */
21031 Roo.data.Node = function(attributes){
21032     /**
21033      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21034      * @type {Object}
21035      */
21036     this.attributes = attributes || {};
21037     this.leaf = this.attributes.leaf;
21038     /**
21039      * The node id. @type String
21040      */
21041     this.id = this.attributes.id;
21042     if(!this.id){
21043         this.id = Roo.id(null, "ynode-");
21044         this.attributes.id = this.id;
21045     }
21046     /**
21047      * All child nodes of this node. @type Array
21048      */
21049     this.childNodes = [];
21050     if(!this.childNodes.indexOf){ // indexOf is a must
21051         this.childNodes.indexOf = function(o){
21052             for(var i = 0, len = this.length; i < len; i++){
21053                 if(this[i] == o) {
21054                     return i;
21055                 }
21056             }
21057             return -1;
21058         };
21059     }
21060     /**
21061      * The parent node for this node. @type Node
21062      */
21063     this.parentNode = null;
21064     /**
21065      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21066      */
21067     this.firstChild = null;
21068     /**
21069      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21070      */
21071     this.lastChild = null;
21072     /**
21073      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21074      */
21075     this.previousSibling = null;
21076     /**
21077      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21078      */
21079     this.nextSibling = null;
21080
21081     this.addEvents({
21082        /**
21083         * @event append
21084         * Fires when a new child node is appended
21085         * @param {Tree} tree The owner tree
21086         * @param {Node} this This node
21087         * @param {Node} node The newly appended node
21088         * @param {Number} index The index of the newly appended node
21089         */
21090        "append" : true,
21091        /**
21092         * @event remove
21093         * Fires when a child node is removed
21094         * @param {Tree} tree The owner tree
21095         * @param {Node} this This node
21096         * @param {Node} node The removed node
21097         */
21098        "remove" : true,
21099        /**
21100         * @event move
21101         * Fires when this node is moved to a new location in the tree
21102         * @param {Tree} tree The owner tree
21103         * @param {Node} this This node
21104         * @param {Node} oldParent The old parent of this node
21105         * @param {Node} newParent The new parent of this node
21106         * @param {Number} index The index it was moved to
21107         */
21108        "move" : true,
21109        /**
21110         * @event insert
21111         * Fires when a new child node is inserted.
21112         * @param {Tree} tree The owner tree
21113         * @param {Node} this This node
21114         * @param {Node} node The child node inserted
21115         * @param {Node} refNode The child node the node was inserted before
21116         */
21117        "insert" : true,
21118        /**
21119         * @event beforeappend
21120         * Fires before a new child is appended, return false to cancel the append.
21121         * @param {Tree} tree The owner tree
21122         * @param {Node} this This node
21123         * @param {Node} node The child node to be appended
21124         */
21125        "beforeappend" : true,
21126        /**
21127         * @event beforeremove
21128         * Fires before a child is removed, return false to cancel the remove.
21129         * @param {Tree} tree The owner tree
21130         * @param {Node} this This node
21131         * @param {Node} node The child node to be removed
21132         */
21133        "beforeremove" : true,
21134        /**
21135         * @event beforemove
21136         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21137         * @param {Tree} tree The owner tree
21138         * @param {Node} this This node
21139         * @param {Node} oldParent The parent of this node
21140         * @param {Node} newParent The new parent this node is moving to
21141         * @param {Number} index The index it is being moved to
21142         */
21143        "beforemove" : true,
21144        /**
21145         * @event beforeinsert
21146         * Fires before a new child is inserted, return false to cancel the insert.
21147         * @param {Tree} tree The owner tree
21148         * @param {Node} this This node
21149         * @param {Node} node The child node to be inserted
21150         * @param {Node} refNode The child node the node is being inserted before
21151         */
21152        "beforeinsert" : true
21153    });
21154     this.listeners = this.attributes.listeners;
21155     Roo.data.Node.superclass.constructor.call(this);
21156 };
21157
21158 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21159     fireEvent : function(evtName){
21160         // first do standard event for this node
21161         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21162             return false;
21163         }
21164         // then bubble it up to the tree if the event wasn't cancelled
21165         var ot = this.getOwnerTree();
21166         if(ot){
21167             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21168                 return false;
21169             }
21170         }
21171         return true;
21172     },
21173
21174     /**
21175      * Returns true if this node is a leaf
21176      * @return {Boolean}
21177      */
21178     isLeaf : function(){
21179         return this.leaf === true;
21180     },
21181
21182     // private
21183     setFirstChild : function(node){
21184         this.firstChild = node;
21185     },
21186
21187     //private
21188     setLastChild : function(node){
21189         this.lastChild = node;
21190     },
21191
21192
21193     /**
21194      * Returns true if this node is the last child of its parent
21195      * @return {Boolean}
21196      */
21197     isLast : function(){
21198        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21199     },
21200
21201     /**
21202      * Returns true if this node is the first child of its parent
21203      * @return {Boolean}
21204      */
21205     isFirst : function(){
21206        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21207     },
21208
21209     hasChildNodes : function(){
21210         return !this.isLeaf() && this.childNodes.length > 0;
21211     },
21212
21213     /**
21214      * Insert node(s) as the last child node of this node.
21215      * @param {Node/Array} node The node or Array of nodes to append
21216      * @return {Node} The appended node if single append, or null if an array was passed
21217      */
21218     appendChild : function(node){
21219         var multi = false;
21220         if(node instanceof Array){
21221             multi = node;
21222         }else if(arguments.length > 1){
21223             multi = arguments;
21224         }
21225         // if passed an array or multiple args do them one by one
21226         if(multi){
21227             for(var i = 0, len = multi.length; i < len; i++) {
21228                 this.appendChild(multi[i]);
21229             }
21230         }else{
21231             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21232                 return false;
21233             }
21234             var index = this.childNodes.length;
21235             var oldParent = node.parentNode;
21236             // it's a move, make sure we move it cleanly
21237             if(oldParent){
21238                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21239                     return false;
21240                 }
21241                 oldParent.removeChild(node);
21242             }
21243             index = this.childNodes.length;
21244             if(index == 0){
21245                 this.setFirstChild(node);
21246             }
21247             this.childNodes.push(node);
21248             node.parentNode = this;
21249             var ps = this.childNodes[index-1];
21250             if(ps){
21251                 node.previousSibling = ps;
21252                 ps.nextSibling = node;
21253             }else{
21254                 node.previousSibling = null;
21255             }
21256             node.nextSibling = null;
21257             this.setLastChild(node);
21258             node.setOwnerTree(this.getOwnerTree());
21259             this.fireEvent("append", this.ownerTree, this, node, index);
21260             if(oldParent){
21261                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21262             }
21263             return node;
21264         }
21265     },
21266
21267     /**
21268      * Removes a child node from this node.
21269      * @param {Node} node The node to remove
21270      * @return {Node} The removed node
21271      */
21272     removeChild : function(node){
21273         var index = this.childNodes.indexOf(node);
21274         if(index == -1){
21275             return false;
21276         }
21277         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21278             return false;
21279         }
21280
21281         // remove it from childNodes collection
21282         this.childNodes.splice(index, 1);
21283
21284         // update siblings
21285         if(node.previousSibling){
21286             node.previousSibling.nextSibling = node.nextSibling;
21287         }
21288         if(node.nextSibling){
21289             node.nextSibling.previousSibling = node.previousSibling;
21290         }
21291
21292         // update child refs
21293         if(this.firstChild == node){
21294             this.setFirstChild(node.nextSibling);
21295         }
21296         if(this.lastChild == node){
21297             this.setLastChild(node.previousSibling);
21298         }
21299
21300         node.setOwnerTree(null);
21301         // clear any references from the node
21302         node.parentNode = null;
21303         node.previousSibling = null;
21304         node.nextSibling = null;
21305         this.fireEvent("remove", this.ownerTree, this, node);
21306         return node;
21307     },
21308
21309     /**
21310      * Inserts the first node before the second node in this nodes childNodes collection.
21311      * @param {Node} node The node to insert
21312      * @param {Node} refNode The node to insert before (if null the node is appended)
21313      * @return {Node} The inserted node
21314      */
21315     insertBefore : function(node, refNode){
21316         if(!refNode){ // like standard Dom, refNode can be null for append
21317             return this.appendChild(node);
21318         }
21319         // nothing to do
21320         if(node == refNode){
21321             return false;
21322         }
21323
21324         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21325             return false;
21326         }
21327         var index = this.childNodes.indexOf(refNode);
21328         var oldParent = node.parentNode;
21329         var refIndex = index;
21330
21331         // when moving internally, indexes will change after remove
21332         if(oldParent == this && this.childNodes.indexOf(node) < index){
21333             refIndex--;
21334         }
21335
21336         // it's a move, make sure we move it cleanly
21337         if(oldParent){
21338             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21339                 return false;
21340             }
21341             oldParent.removeChild(node);
21342         }
21343         if(refIndex == 0){
21344             this.setFirstChild(node);
21345         }
21346         this.childNodes.splice(refIndex, 0, node);
21347         node.parentNode = this;
21348         var ps = this.childNodes[refIndex-1];
21349         if(ps){
21350             node.previousSibling = ps;
21351             ps.nextSibling = node;
21352         }else{
21353             node.previousSibling = null;
21354         }
21355         node.nextSibling = refNode;
21356         refNode.previousSibling = node;
21357         node.setOwnerTree(this.getOwnerTree());
21358         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21359         if(oldParent){
21360             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21361         }
21362         return node;
21363     },
21364
21365     /**
21366      * Returns the child node at the specified index.
21367      * @param {Number} index
21368      * @return {Node}
21369      */
21370     item : function(index){
21371         return this.childNodes[index];
21372     },
21373
21374     /**
21375      * Replaces one child node in this node with another.
21376      * @param {Node} newChild The replacement node
21377      * @param {Node} oldChild The node to replace
21378      * @return {Node} The replaced node
21379      */
21380     replaceChild : function(newChild, oldChild){
21381         this.insertBefore(newChild, oldChild);
21382         this.removeChild(oldChild);
21383         return oldChild;
21384     },
21385
21386     /**
21387      * Returns the index of a child node
21388      * @param {Node} node
21389      * @return {Number} The index of the node or -1 if it was not found
21390      */
21391     indexOf : function(child){
21392         return this.childNodes.indexOf(child);
21393     },
21394
21395     /**
21396      * Returns the tree this node is in.
21397      * @return {Tree}
21398      */
21399     getOwnerTree : function(){
21400         // if it doesn't have one, look for one
21401         if(!this.ownerTree){
21402             var p = this;
21403             while(p){
21404                 if(p.ownerTree){
21405                     this.ownerTree = p.ownerTree;
21406                     break;
21407                 }
21408                 p = p.parentNode;
21409             }
21410         }
21411         return this.ownerTree;
21412     },
21413
21414     /**
21415      * Returns depth of this node (the root node has a depth of 0)
21416      * @return {Number}
21417      */
21418     getDepth : function(){
21419         var depth = 0;
21420         var p = this;
21421         while(p.parentNode){
21422             ++depth;
21423             p = p.parentNode;
21424         }
21425         return depth;
21426     },
21427
21428     // private
21429     setOwnerTree : function(tree){
21430         // if it's move, we need to update everyone
21431         if(tree != this.ownerTree){
21432             if(this.ownerTree){
21433                 this.ownerTree.unregisterNode(this);
21434             }
21435             this.ownerTree = tree;
21436             var cs = this.childNodes;
21437             for(var i = 0, len = cs.length; i < len; i++) {
21438                 cs[i].setOwnerTree(tree);
21439             }
21440             if(tree){
21441                 tree.registerNode(this);
21442             }
21443         }
21444     },
21445
21446     /**
21447      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21448      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21449      * @return {String} The path
21450      */
21451     getPath : function(attr){
21452         attr = attr || "id";
21453         var p = this.parentNode;
21454         var b = [this.attributes[attr]];
21455         while(p){
21456             b.unshift(p.attributes[attr]);
21457             p = p.parentNode;
21458         }
21459         var sep = this.getOwnerTree().pathSeparator;
21460         return sep + b.join(sep);
21461     },
21462
21463     /**
21464      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21465      * function call will be the scope provided or the current node. The arguments to the function
21466      * will be the args provided or the current node. If the function returns false at any point,
21467      * the bubble is stopped.
21468      * @param {Function} fn The function to call
21469      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21470      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21471      */
21472     bubble : function(fn, scope, args){
21473         var p = this;
21474         while(p){
21475             if(fn.call(scope || p, args || p) === false){
21476                 break;
21477             }
21478             p = p.parentNode;
21479         }
21480     },
21481
21482     /**
21483      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21484      * function call will be the scope provided or the current node. The arguments to the function
21485      * will be the args provided or the current node. If the function returns false at any point,
21486      * the cascade is stopped on that branch.
21487      * @param {Function} fn The function to call
21488      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21489      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21490      */
21491     cascade : function(fn, scope, args){
21492         if(fn.call(scope || this, args || this) !== false){
21493             var cs = this.childNodes;
21494             for(var i = 0, len = cs.length; i < len; i++) {
21495                 cs[i].cascade(fn, scope, args);
21496             }
21497         }
21498     },
21499
21500     /**
21501      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21502      * function call will be the scope provided or the current node. The arguments to the function
21503      * will be the args provided or the current node. If the function returns false at any point,
21504      * the iteration stops.
21505      * @param {Function} fn The function to call
21506      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21507      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21508      */
21509     eachChild : function(fn, scope, args){
21510         var cs = this.childNodes;
21511         for(var i = 0, len = cs.length; i < len; i++) {
21512                 if(fn.call(scope || this, args || cs[i]) === false){
21513                     break;
21514                 }
21515         }
21516     },
21517
21518     /**
21519      * Finds the first child that has the attribute with the specified value.
21520      * @param {String} attribute The attribute name
21521      * @param {Mixed} value The value to search for
21522      * @return {Node} The found child or null if none was found
21523      */
21524     findChild : function(attribute, value){
21525         var cs = this.childNodes;
21526         for(var i = 0, len = cs.length; i < len; i++) {
21527                 if(cs[i].attributes[attribute] == value){
21528                     return cs[i];
21529                 }
21530         }
21531         return null;
21532     },
21533
21534     /**
21535      * Finds the first child by a custom function. The child matches if the function passed
21536      * returns true.
21537      * @param {Function} fn
21538      * @param {Object} scope (optional)
21539      * @return {Node} The found child or null if none was found
21540      */
21541     findChildBy : function(fn, scope){
21542         var cs = this.childNodes;
21543         for(var i = 0, len = cs.length; i < len; i++) {
21544                 if(fn.call(scope||cs[i], cs[i]) === true){
21545                     return cs[i];
21546                 }
21547         }
21548         return null;
21549     },
21550
21551     /**
21552      * Sorts this nodes children using the supplied sort function
21553      * @param {Function} fn
21554      * @param {Object} scope (optional)
21555      */
21556     sort : function(fn, scope){
21557         var cs = this.childNodes;
21558         var len = cs.length;
21559         if(len > 0){
21560             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21561             cs.sort(sortFn);
21562             for(var i = 0; i < len; i++){
21563                 var n = cs[i];
21564                 n.previousSibling = cs[i-1];
21565                 n.nextSibling = cs[i+1];
21566                 if(i == 0){
21567                     this.setFirstChild(n);
21568                 }
21569                 if(i == len-1){
21570                     this.setLastChild(n);
21571                 }
21572             }
21573         }
21574     },
21575
21576     /**
21577      * Returns true if this node is an ancestor (at any point) of the passed node.
21578      * @param {Node} node
21579      * @return {Boolean}
21580      */
21581     contains : function(node){
21582         return node.isAncestor(this);
21583     },
21584
21585     /**
21586      * Returns true if the passed node is an ancestor (at any point) of this node.
21587      * @param {Node} node
21588      * @return {Boolean}
21589      */
21590     isAncestor : function(node){
21591         var p = this.parentNode;
21592         while(p){
21593             if(p == node){
21594                 return true;
21595             }
21596             p = p.parentNode;
21597         }
21598         return false;
21599     },
21600
21601     toString : function(){
21602         return "[Node"+(this.id?" "+this.id:"")+"]";
21603     }
21604 });/*
21605  * Based on:
21606  * Ext JS Library 1.1.1
21607  * Copyright(c) 2006-2007, Ext JS, LLC.
21608  *
21609  * Originally Released Under LGPL - original licence link has changed is not relivant.
21610  *
21611  * Fork - LGPL
21612  * <script type="text/javascript">
21613  */
21614  
21615
21616 /**
21617  * @class Roo.ComponentMgr
21618  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21619  * @singleton
21620  */
21621 Roo.ComponentMgr = function(){
21622     var all = new Roo.util.MixedCollection();
21623
21624     return {
21625         /**
21626          * Registers a component.
21627          * @param {Roo.Component} c The component
21628          */
21629         register : function(c){
21630             all.add(c);
21631         },
21632
21633         /**
21634          * Unregisters a component.
21635          * @param {Roo.Component} c The component
21636          */
21637         unregister : function(c){
21638             all.remove(c);
21639         },
21640
21641         /**
21642          * Returns a component by id
21643          * @param {String} id The component id
21644          */
21645         get : function(id){
21646             return all.get(id);
21647         },
21648
21649         /**
21650          * Registers a function that will be called when a specified component is added to ComponentMgr
21651          * @param {String} id The component id
21652          * @param {Funtction} fn The callback function
21653          * @param {Object} scope The scope of the callback
21654          */
21655         onAvailable : function(id, fn, scope){
21656             all.on("add", function(index, o){
21657                 if(o.id == id){
21658                     fn.call(scope || o, o);
21659                     all.un("add", fn, scope);
21660                 }
21661             });
21662         }
21663     };
21664 }();/*
21665  * Based on:
21666  * Ext JS Library 1.1.1
21667  * Copyright(c) 2006-2007, Ext JS, LLC.
21668  *
21669  * Originally Released Under LGPL - original licence link has changed is not relivant.
21670  *
21671  * Fork - LGPL
21672  * <script type="text/javascript">
21673  */
21674  
21675 /**
21676  * @class Roo.Component
21677  * @extends Roo.util.Observable
21678  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21679  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21680  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21681  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21682  * All visual components (widgets) that require rendering into a layout should subclass Component.
21683  * @constructor
21684  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21685  * 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
21686  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21687  */
21688 Roo.Component = function(config){
21689     config = config || {};
21690     if(config.tagName || config.dom || typeof config == "string"){ // element object
21691         config = {el: config, id: config.id || config};
21692     }
21693     this.initialConfig = config;
21694
21695     Roo.apply(this, config);
21696     this.addEvents({
21697         /**
21698          * @event disable
21699          * Fires after the component is disabled.
21700              * @param {Roo.Component} this
21701              */
21702         disable : true,
21703         /**
21704          * @event enable
21705          * Fires after the component is enabled.
21706              * @param {Roo.Component} this
21707              */
21708         enable : true,
21709         /**
21710          * @event beforeshow
21711          * Fires before the component is shown.  Return false to stop the show.
21712              * @param {Roo.Component} this
21713              */
21714         beforeshow : true,
21715         /**
21716          * @event show
21717          * Fires after the component is shown.
21718              * @param {Roo.Component} this
21719              */
21720         show : true,
21721         /**
21722          * @event beforehide
21723          * Fires before the component is hidden. Return false to stop the hide.
21724              * @param {Roo.Component} this
21725              */
21726         beforehide : true,
21727         /**
21728          * @event hide
21729          * Fires after the component is hidden.
21730              * @param {Roo.Component} this
21731              */
21732         hide : true,
21733         /**
21734          * @event beforerender
21735          * Fires before the component is rendered. Return false to stop the render.
21736              * @param {Roo.Component} this
21737              */
21738         beforerender : true,
21739         /**
21740          * @event render
21741          * Fires after the component is rendered.
21742              * @param {Roo.Component} this
21743              */
21744         render : true,
21745         /**
21746          * @event beforedestroy
21747          * Fires before the component is destroyed. Return false to stop the destroy.
21748              * @param {Roo.Component} this
21749              */
21750         beforedestroy : true,
21751         /**
21752          * @event destroy
21753          * Fires after the component is destroyed.
21754              * @param {Roo.Component} this
21755              */
21756         destroy : true
21757     });
21758     if(!this.id){
21759         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21760     }
21761     Roo.ComponentMgr.register(this);
21762     Roo.Component.superclass.constructor.call(this);
21763     this.initComponent();
21764     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21765         this.render(this.renderTo);
21766         delete this.renderTo;
21767     }
21768 };
21769
21770 // private
21771 Roo.Component.AUTO_ID = 1000;
21772
21773 Roo.extend(Roo.Component, Roo.util.Observable, {
21774     /**
21775      * @property {Boolean} hidden
21776      * true if this component is hidden. Read-only.
21777      */
21778     hidden : false,
21779     /**
21780      * true if this component is disabled. Read-only.
21781      */
21782     disabled : false,
21783     /**
21784      * true if this component has been rendered. Read-only.
21785      */
21786     rendered : false,
21787     
21788     /** @cfg {String} disableClass
21789      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21790      */
21791     disabledClass : "x-item-disabled",
21792         /** @cfg {Boolean} allowDomMove
21793          * Whether the component can move the Dom node when rendering (defaults to true).
21794          */
21795     allowDomMove : true,
21796     /** @cfg {String} hideMode
21797      * How this component should hidden. Supported values are
21798      * "visibility" (css visibility), "offsets" (negative offset position) and
21799      * "display" (css display) - defaults to "display".
21800      */
21801     hideMode: 'display',
21802
21803     // private
21804     ctype : "Roo.Component",
21805
21806     /** @cfg {String} actionMode 
21807      * which property holds the element that used for  hide() / show() / disable() / enable()
21808      * default is 'el' 
21809      */
21810     actionMode : "el",
21811
21812     // private
21813     getActionEl : function(){
21814         return this[this.actionMode];
21815     },
21816
21817     initComponent : Roo.emptyFn,
21818     /**
21819      * If this is a lazy rendering component, render it to its container element.
21820      * @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.
21821      */
21822     render : function(container, position){
21823         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21824             if(!container && this.el){
21825                 this.el = Roo.get(this.el);
21826                 container = this.el.dom.parentNode;
21827                 this.allowDomMove = false;
21828             }
21829             this.container = Roo.get(container);
21830             this.rendered = true;
21831             if(position !== undefined){
21832                 if(typeof position == 'number'){
21833                     position = this.container.dom.childNodes[position];
21834                 }else{
21835                     position = Roo.getDom(position);
21836                 }
21837             }
21838             this.onRender(this.container, position || null);
21839             if(this.cls){
21840                 this.el.addClass(this.cls);
21841                 delete this.cls;
21842             }
21843             if(this.style){
21844                 this.el.applyStyles(this.style);
21845                 delete this.style;
21846             }
21847             this.fireEvent("render", this);
21848             this.afterRender(this.container);
21849             if(this.hidden){
21850                 this.hide();
21851             }
21852             if(this.disabled){
21853                 this.disable();
21854             }
21855         }
21856         return this;
21857     },
21858
21859     // private
21860     // default function is not really useful
21861     onRender : function(ct, position){
21862         if(this.el){
21863             this.el = Roo.get(this.el);
21864             if(this.allowDomMove !== false){
21865                 ct.dom.insertBefore(this.el.dom, position);
21866             }
21867         }
21868     },
21869
21870     // private
21871     getAutoCreate : function(){
21872         var cfg = typeof this.autoCreate == "object" ?
21873                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21874         if(this.id && !cfg.id){
21875             cfg.id = this.id;
21876         }
21877         return cfg;
21878     },
21879
21880     // private
21881     afterRender : Roo.emptyFn,
21882
21883     /**
21884      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21885      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21886      */
21887     destroy : function(){
21888         if(this.fireEvent("beforedestroy", this) !== false){
21889             this.purgeListeners();
21890             this.beforeDestroy();
21891             if(this.rendered){
21892                 this.el.removeAllListeners();
21893                 this.el.remove();
21894                 if(this.actionMode == "container"){
21895                     this.container.remove();
21896                 }
21897             }
21898             this.onDestroy();
21899             Roo.ComponentMgr.unregister(this);
21900             this.fireEvent("destroy", this);
21901         }
21902     },
21903
21904         // private
21905     beforeDestroy : function(){
21906
21907     },
21908
21909         // private
21910         onDestroy : function(){
21911
21912     },
21913
21914     /**
21915      * Returns the underlying {@link Roo.Element}.
21916      * @return {Roo.Element} The element
21917      */
21918     getEl : function(){
21919         return this.el;
21920     },
21921
21922     /**
21923      * Returns the id of this component.
21924      * @return {String}
21925      */
21926     getId : function(){
21927         return this.id;
21928     },
21929
21930     /**
21931      * Try to focus this component.
21932      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21933      * @return {Roo.Component} this
21934      */
21935     focus : function(selectText){
21936         if(this.rendered){
21937             this.el.focus();
21938             if(selectText === true){
21939                 this.el.dom.select();
21940             }
21941         }
21942         return this;
21943     },
21944
21945     // private
21946     blur : function(){
21947         if(this.rendered){
21948             this.el.blur();
21949         }
21950         return this;
21951     },
21952
21953     /**
21954      * Disable this component.
21955      * @return {Roo.Component} this
21956      */
21957     disable : function(){
21958         if(this.rendered){
21959             this.onDisable();
21960         }
21961         this.disabled = true;
21962         this.fireEvent("disable", this);
21963         return this;
21964     },
21965
21966         // private
21967     onDisable : function(){
21968         this.getActionEl().addClass(this.disabledClass);
21969         this.el.dom.disabled = true;
21970     },
21971
21972     /**
21973      * Enable this component.
21974      * @return {Roo.Component} this
21975      */
21976     enable : function(){
21977         if(this.rendered){
21978             this.onEnable();
21979         }
21980         this.disabled = false;
21981         this.fireEvent("enable", this);
21982         return this;
21983     },
21984
21985         // private
21986     onEnable : function(){
21987         this.getActionEl().removeClass(this.disabledClass);
21988         this.el.dom.disabled = false;
21989     },
21990
21991     /**
21992      * Convenience function for setting disabled/enabled by boolean.
21993      * @param {Boolean} disabled
21994      */
21995     setDisabled : function(disabled){
21996         this[disabled ? "disable" : "enable"]();
21997     },
21998
21999     /**
22000      * Show this component.
22001      * @return {Roo.Component} this
22002      */
22003     show: function(){
22004         if(this.fireEvent("beforeshow", this) !== false){
22005             this.hidden = false;
22006             if(this.rendered){
22007                 this.onShow();
22008             }
22009             this.fireEvent("show", this);
22010         }
22011         return this;
22012     },
22013
22014     // private
22015     onShow : function(){
22016         var ae = this.getActionEl();
22017         if(this.hideMode == 'visibility'){
22018             ae.dom.style.visibility = "visible";
22019         }else if(this.hideMode == 'offsets'){
22020             ae.removeClass('x-hidden');
22021         }else{
22022             ae.dom.style.display = "";
22023         }
22024     },
22025
22026     /**
22027      * Hide this component.
22028      * @return {Roo.Component} this
22029      */
22030     hide: function(){
22031         if(this.fireEvent("beforehide", this) !== false){
22032             this.hidden = true;
22033             if(this.rendered){
22034                 this.onHide();
22035             }
22036             this.fireEvent("hide", this);
22037         }
22038         return this;
22039     },
22040
22041     // private
22042     onHide : function(){
22043         var ae = this.getActionEl();
22044         if(this.hideMode == 'visibility'){
22045             ae.dom.style.visibility = "hidden";
22046         }else if(this.hideMode == 'offsets'){
22047             ae.addClass('x-hidden');
22048         }else{
22049             ae.dom.style.display = "none";
22050         }
22051     },
22052
22053     /**
22054      * Convenience function to hide or show this component by boolean.
22055      * @param {Boolean} visible True to show, false to hide
22056      * @return {Roo.Component} this
22057      */
22058     setVisible: function(visible){
22059         if(visible) {
22060             this.show();
22061         }else{
22062             this.hide();
22063         }
22064         return this;
22065     },
22066
22067     /**
22068      * Returns true if this component is visible.
22069      */
22070     isVisible : function(){
22071         return this.getActionEl().isVisible();
22072     },
22073
22074     cloneConfig : function(overrides){
22075         overrides = overrides || {};
22076         var id = overrides.id || Roo.id();
22077         var cfg = Roo.applyIf(overrides, this.initialConfig);
22078         cfg.id = id; // prevent dup id
22079         return new this.constructor(cfg);
22080     }
22081 });/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091  (function(){ 
22092 /**
22093  * @class Roo.Layer
22094  * @extends Roo.Element
22095  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22096  * automatic maintaining of shadow/shim positions.
22097  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22098  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22099  * you can pass a string with a CSS class name. False turns off the shadow.
22100  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22101  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22102  * @cfg {String} cls CSS class to add to the element
22103  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22104  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22105  * @constructor
22106  * @param {Object} config An object with config options.
22107  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22108  */
22109
22110 Roo.Layer = function(config, existingEl){
22111     config = config || {};
22112     var dh = Roo.DomHelper;
22113     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22114     if(existingEl){
22115         this.dom = Roo.getDom(existingEl);
22116     }
22117     if(!this.dom){
22118         var o = config.dh || {tag: "div", cls: "x-layer"};
22119         this.dom = dh.append(pel, o);
22120     }
22121     if(config.cls){
22122         this.addClass(config.cls);
22123     }
22124     this.constrain = config.constrain !== false;
22125     this.visibilityMode = Roo.Element.VISIBILITY;
22126     if(config.id){
22127         this.id = this.dom.id = config.id;
22128     }else{
22129         this.id = Roo.id(this.dom);
22130     }
22131     this.zindex = config.zindex || this.getZIndex();
22132     this.position("absolute", this.zindex);
22133     if(config.shadow){
22134         this.shadowOffset = config.shadowOffset || 4;
22135         this.shadow = new Roo.Shadow({
22136             offset : this.shadowOffset,
22137             mode : config.shadow
22138         });
22139     }else{
22140         this.shadowOffset = 0;
22141     }
22142     this.useShim = config.shim !== false && Roo.useShims;
22143     this.useDisplay = config.useDisplay;
22144     this.hide();
22145 };
22146
22147 var supr = Roo.Element.prototype;
22148
22149 // shims are shared among layer to keep from having 100 iframes
22150 var shims = [];
22151
22152 Roo.extend(Roo.Layer, Roo.Element, {
22153
22154     getZIndex : function(){
22155         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22156     },
22157
22158     getShim : function(){
22159         if(!this.useShim){
22160             return null;
22161         }
22162         if(this.shim){
22163             return this.shim;
22164         }
22165         var shim = shims.shift();
22166         if(!shim){
22167             shim = this.createShim();
22168             shim.enableDisplayMode('block');
22169             shim.dom.style.display = 'none';
22170             shim.dom.style.visibility = 'visible';
22171         }
22172         var pn = this.dom.parentNode;
22173         if(shim.dom.parentNode != pn){
22174             pn.insertBefore(shim.dom, this.dom);
22175         }
22176         shim.setStyle('z-index', this.getZIndex()-2);
22177         this.shim = shim;
22178         return shim;
22179     },
22180
22181     hideShim : function(){
22182         if(this.shim){
22183             this.shim.setDisplayed(false);
22184             shims.push(this.shim);
22185             delete this.shim;
22186         }
22187     },
22188
22189     disableShadow : function(){
22190         if(this.shadow){
22191             this.shadowDisabled = true;
22192             this.shadow.hide();
22193             this.lastShadowOffset = this.shadowOffset;
22194             this.shadowOffset = 0;
22195         }
22196     },
22197
22198     enableShadow : function(show){
22199         if(this.shadow){
22200             this.shadowDisabled = false;
22201             this.shadowOffset = this.lastShadowOffset;
22202             delete this.lastShadowOffset;
22203             if(show){
22204                 this.sync(true);
22205             }
22206         }
22207     },
22208
22209     // private
22210     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22211     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22212     sync : function(doShow){
22213         var sw = this.shadow;
22214         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22215             var sh = this.getShim();
22216
22217             var w = this.getWidth(),
22218                 h = this.getHeight();
22219
22220             var l = this.getLeft(true),
22221                 t = this.getTop(true);
22222
22223             if(sw && !this.shadowDisabled){
22224                 if(doShow && !sw.isVisible()){
22225                     sw.show(this);
22226                 }else{
22227                     sw.realign(l, t, w, h);
22228                 }
22229                 if(sh){
22230                     if(doShow){
22231                        sh.show();
22232                     }
22233                     // fit the shim behind the shadow, so it is shimmed too
22234                     var a = sw.adjusts, s = sh.dom.style;
22235                     s.left = (Math.min(l, l+a.l))+"px";
22236                     s.top = (Math.min(t, t+a.t))+"px";
22237                     s.width = (w+a.w)+"px";
22238                     s.height = (h+a.h)+"px";
22239                 }
22240             }else if(sh){
22241                 if(doShow){
22242                    sh.show();
22243                 }
22244                 sh.setSize(w, h);
22245                 sh.setLeftTop(l, t);
22246             }
22247             
22248         }
22249     },
22250
22251     // private
22252     destroy : function(){
22253         this.hideShim();
22254         if(this.shadow){
22255             this.shadow.hide();
22256         }
22257         this.removeAllListeners();
22258         var pn = this.dom.parentNode;
22259         if(pn){
22260             pn.removeChild(this.dom);
22261         }
22262         Roo.Element.uncache(this.id);
22263     },
22264
22265     remove : function(){
22266         this.destroy();
22267     },
22268
22269     // private
22270     beginUpdate : function(){
22271         this.updating = true;
22272     },
22273
22274     // private
22275     endUpdate : function(){
22276         this.updating = false;
22277         this.sync(true);
22278     },
22279
22280     // private
22281     hideUnders : function(negOffset){
22282         if(this.shadow){
22283             this.shadow.hide();
22284         }
22285         this.hideShim();
22286     },
22287
22288     // private
22289     constrainXY : function(){
22290         if(this.constrain){
22291             var vw = Roo.lib.Dom.getViewWidth(),
22292                 vh = Roo.lib.Dom.getViewHeight();
22293             var s = Roo.get(document).getScroll();
22294
22295             var xy = this.getXY();
22296             var x = xy[0], y = xy[1];   
22297             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22298             // only move it if it needs it
22299             var moved = false;
22300             // first validate right/bottom
22301             if((x + w) > vw+s.left){
22302                 x = vw - w - this.shadowOffset;
22303                 moved = true;
22304             }
22305             if((y + h) > vh+s.top){
22306                 y = vh - h - this.shadowOffset;
22307                 moved = true;
22308             }
22309             // then make sure top/left isn't negative
22310             if(x < s.left){
22311                 x = s.left;
22312                 moved = true;
22313             }
22314             if(y < s.top){
22315                 y = s.top;
22316                 moved = true;
22317             }
22318             if(moved){
22319                 if(this.avoidY){
22320                     var ay = this.avoidY;
22321                     if(y <= ay && (y+h) >= ay){
22322                         y = ay-h-5;   
22323                     }
22324                 }
22325                 xy = [x, y];
22326                 this.storeXY(xy);
22327                 supr.setXY.call(this, xy);
22328                 this.sync();
22329             }
22330         }
22331     },
22332
22333     isVisible : function(){
22334         return this.visible;    
22335     },
22336
22337     // private
22338     showAction : function(){
22339         this.visible = true; // track visibility to prevent getStyle calls
22340         if(this.useDisplay === true){
22341             this.setDisplayed("");
22342         }else if(this.lastXY){
22343             supr.setXY.call(this, this.lastXY);
22344         }else if(this.lastLT){
22345             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22346         }
22347     },
22348
22349     // private
22350     hideAction : function(){
22351         this.visible = false;
22352         if(this.useDisplay === true){
22353             this.setDisplayed(false);
22354         }else{
22355             this.setLeftTop(-10000,-10000);
22356         }
22357     },
22358
22359     // overridden Element method
22360     setVisible : function(v, a, d, c, e){
22361         if(v){
22362             this.showAction();
22363         }
22364         if(a && v){
22365             var cb = function(){
22366                 this.sync(true);
22367                 if(c){
22368                     c();
22369                 }
22370             }.createDelegate(this);
22371             supr.setVisible.call(this, true, true, d, cb, e);
22372         }else{
22373             if(!v){
22374                 this.hideUnders(true);
22375             }
22376             var cb = c;
22377             if(a){
22378                 cb = function(){
22379                     this.hideAction();
22380                     if(c){
22381                         c();
22382                     }
22383                 }.createDelegate(this);
22384             }
22385             supr.setVisible.call(this, v, a, d, cb, e);
22386             if(v){
22387                 this.sync(true);
22388             }else if(!a){
22389                 this.hideAction();
22390             }
22391         }
22392     },
22393
22394     storeXY : function(xy){
22395         delete this.lastLT;
22396         this.lastXY = xy;
22397     },
22398
22399     storeLeftTop : function(left, top){
22400         delete this.lastXY;
22401         this.lastLT = [left, top];
22402     },
22403
22404     // private
22405     beforeFx : function(){
22406         this.beforeAction();
22407         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22408     },
22409
22410     // private
22411     afterFx : function(){
22412         Roo.Layer.superclass.afterFx.apply(this, arguments);
22413         this.sync(this.isVisible());
22414     },
22415
22416     // private
22417     beforeAction : function(){
22418         if(!this.updating && this.shadow){
22419             this.shadow.hide();
22420         }
22421     },
22422
22423     // overridden Element method
22424     setLeft : function(left){
22425         this.storeLeftTop(left, this.getTop(true));
22426         supr.setLeft.apply(this, arguments);
22427         this.sync();
22428     },
22429
22430     setTop : function(top){
22431         this.storeLeftTop(this.getLeft(true), top);
22432         supr.setTop.apply(this, arguments);
22433         this.sync();
22434     },
22435
22436     setLeftTop : function(left, top){
22437         this.storeLeftTop(left, top);
22438         supr.setLeftTop.apply(this, arguments);
22439         this.sync();
22440     },
22441
22442     setXY : function(xy, a, d, c, e){
22443         this.fixDisplay();
22444         this.beforeAction();
22445         this.storeXY(xy);
22446         var cb = this.createCB(c);
22447         supr.setXY.call(this, xy, a, d, cb, e);
22448         if(!a){
22449             cb();
22450         }
22451     },
22452
22453     // private
22454     createCB : function(c){
22455         var el = this;
22456         return function(){
22457             el.constrainXY();
22458             el.sync(true);
22459             if(c){
22460                 c();
22461             }
22462         };
22463     },
22464
22465     // overridden Element method
22466     setX : function(x, a, d, c, e){
22467         this.setXY([x, this.getY()], a, d, c, e);
22468     },
22469
22470     // overridden Element method
22471     setY : function(y, a, d, c, e){
22472         this.setXY([this.getX(), y], a, d, c, e);
22473     },
22474
22475     // overridden Element method
22476     setSize : function(w, h, a, d, c, e){
22477         this.beforeAction();
22478         var cb = this.createCB(c);
22479         supr.setSize.call(this, w, h, a, d, cb, e);
22480         if(!a){
22481             cb();
22482         }
22483     },
22484
22485     // overridden Element method
22486     setWidth : function(w, a, d, c, e){
22487         this.beforeAction();
22488         var cb = this.createCB(c);
22489         supr.setWidth.call(this, w, a, d, cb, e);
22490         if(!a){
22491             cb();
22492         }
22493     },
22494
22495     // overridden Element method
22496     setHeight : function(h, a, d, c, e){
22497         this.beforeAction();
22498         var cb = this.createCB(c);
22499         supr.setHeight.call(this, h, a, d, cb, e);
22500         if(!a){
22501             cb();
22502         }
22503     },
22504
22505     // overridden Element method
22506     setBounds : function(x, y, w, h, a, d, c, e){
22507         this.beforeAction();
22508         var cb = this.createCB(c);
22509         if(!a){
22510             this.storeXY([x, y]);
22511             supr.setXY.call(this, [x, y]);
22512             supr.setSize.call(this, w, h, a, d, cb, e);
22513             cb();
22514         }else{
22515             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22516         }
22517         return this;
22518     },
22519     
22520     /**
22521      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22522      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22523      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22524      * @param {Number} zindex The new z-index to set
22525      * @return {this} The Layer
22526      */
22527     setZIndex : function(zindex){
22528         this.zindex = zindex;
22529         this.setStyle("z-index", zindex + 2);
22530         if(this.shadow){
22531             this.shadow.setZIndex(zindex + 1);
22532         }
22533         if(this.shim){
22534             this.shim.setStyle("z-index", zindex);
22535         }
22536     }
22537 });
22538 })();/*
22539  * Based on:
22540  * Ext JS Library 1.1.1
22541  * Copyright(c) 2006-2007, Ext JS, LLC.
22542  *
22543  * Originally Released Under LGPL - original licence link has changed is not relivant.
22544  *
22545  * Fork - LGPL
22546  * <script type="text/javascript">
22547  */
22548
22549
22550 /**
22551  * @class Roo.Shadow
22552  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22553  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22554  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22555  * @constructor
22556  * Create a new Shadow
22557  * @param {Object} config The config object
22558  */
22559 Roo.Shadow = function(config){
22560     Roo.apply(this, config);
22561     if(typeof this.mode != "string"){
22562         this.mode = this.defaultMode;
22563     }
22564     var o = this.offset, a = {h: 0};
22565     var rad = Math.floor(this.offset/2);
22566     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22567         case "drop":
22568             a.w = 0;
22569             a.l = a.t = o;
22570             a.t -= 1;
22571             if(Roo.isIE){
22572                 a.l -= this.offset + rad;
22573                 a.t -= this.offset + rad;
22574                 a.w -= rad;
22575                 a.h -= rad;
22576                 a.t += 1;
22577             }
22578         break;
22579         case "sides":
22580             a.w = (o*2);
22581             a.l = -o;
22582             a.t = o-1;
22583             if(Roo.isIE){
22584                 a.l -= (this.offset - rad);
22585                 a.t -= this.offset + rad;
22586                 a.l += 1;
22587                 a.w -= (this.offset - rad)*2;
22588                 a.w -= rad + 1;
22589                 a.h -= 1;
22590             }
22591         break;
22592         case "frame":
22593             a.w = a.h = (o*2);
22594             a.l = a.t = -o;
22595             a.t += 1;
22596             a.h -= 2;
22597             if(Roo.isIE){
22598                 a.l -= (this.offset - rad);
22599                 a.t -= (this.offset - rad);
22600                 a.l += 1;
22601                 a.w -= (this.offset + rad + 1);
22602                 a.h -= (this.offset + rad);
22603                 a.h += 1;
22604             }
22605         break;
22606     };
22607
22608     this.adjusts = a;
22609 };
22610
22611 Roo.Shadow.prototype = {
22612     /**
22613      * @cfg {String} mode
22614      * The shadow display mode.  Supports the following options:<br />
22615      * sides: Shadow displays on both sides and bottom only<br />
22616      * frame: Shadow displays equally on all four sides<br />
22617      * drop: Traditional bottom-right drop shadow (default)
22618      */
22619     /**
22620      * @cfg {String} offset
22621      * The number of pixels to offset the shadow from the element (defaults to 4)
22622      */
22623     offset: 4,
22624
22625     // private
22626     defaultMode: "drop",
22627
22628     /**
22629      * Displays the shadow under the target element
22630      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22631      */
22632     show : function(target){
22633         target = Roo.get(target);
22634         if(!this.el){
22635             this.el = Roo.Shadow.Pool.pull();
22636             if(this.el.dom.nextSibling != target.dom){
22637                 this.el.insertBefore(target);
22638             }
22639         }
22640         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22641         if(Roo.isIE){
22642             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22643         }
22644         this.realign(
22645             target.getLeft(true),
22646             target.getTop(true),
22647             target.getWidth(),
22648             target.getHeight()
22649         );
22650         this.el.dom.style.display = "block";
22651     },
22652
22653     /**
22654      * Returns true if the shadow is visible, else false
22655      */
22656     isVisible : function(){
22657         return this.el ? true : false;  
22658     },
22659
22660     /**
22661      * Direct alignment when values are already available. Show must be called at least once before
22662      * calling this method to ensure it is initialized.
22663      * @param {Number} left The target element left position
22664      * @param {Number} top The target element top position
22665      * @param {Number} width The target element width
22666      * @param {Number} height The target element height
22667      */
22668     realign : function(l, t, w, h){
22669         if(!this.el){
22670             return;
22671         }
22672         var a = this.adjusts, d = this.el.dom, s = d.style;
22673         var iea = 0;
22674         s.left = (l+a.l)+"px";
22675         s.top = (t+a.t)+"px";
22676         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22677  
22678         if(s.width != sws || s.height != shs){
22679             s.width = sws;
22680             s.height = shs;
22681             if(!Roo.isIE){
22682                 var cn = d.childNodes;
22683                 var sww = Math.max(0, (sw-12))+"px";
22684                 cn[0].childNodes[1].style.width = sww;
22685                 cn[1].childNodes[1].style.width = sww;
22686                 cn[2].childNodes[1].style.width = sww;
22687                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22688             }
22689         }
22690     },
22691
22692     /**
22693      * Hides this shadow
22694      */
22695     hide : function(){
22696         if(this.el){
22697             this.el.dom.style.display = "none";
22698             Roo.Shadow.Pool.push(this.el);
22699             delete this.el;
22700         }
22701     },
22702
22703     /**
22704      * Adjust the z-index of this shadow
22705      * @param {Number} zindex The new z-index
22706      */
22707     setZIndex : function(z){
22708         this.zIndex = z;
22709         if(this.el){
22710             this.el.setStyle("z-index", z);
22711         }
22712     }
22713 };
22714
22715 // Private utility class that manages the internal Shadow cache
22716 Roo.Shadow.Pool = function(){
22717     var p = [];
22718     var markup = Roo.isIE ?
22719                  '<div class="x-ie-shadow"></div>' :
22720                  '<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>';
22721     return {
22722         pull : function(){
22723             var sh = p.shift();
22724             if(!sh){
22725                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22726                 sh.autoBoxAdjust = false;
22727             }
22728             return sh;
22729         },
22730
22731         push : function(sh){
22732             p.push(sh);
22733         }
22734     };
22735 }();/*
22736  * Based on:
22737  * Ext JS Library 1.1.1
22738  * Copyright(c) 2006-2007, Ext JS, LLC.
22739  *
22740  * Originally Released Under LGPL - original licence link has changed is not relivant.
22741  *
22742  * Fork - LGPL
22743  * <script type="text/javascript">
22744  */
22745
22746 /**
22747  * @class Roo.BoxComponent
22748  * @extends Roo.Component
22749  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22750  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22751  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22752  * layout containers.
22753  * @constructor
22754  * @param {Roo.Element/String/Object} config The configuration options.
22755  */
22756 Roo.BoxComponent = function(config){
22757     Roo.Component.call(this, config);
22758     this.addEvents({
22759         /**
22760          * @event resize
22761          * Fires after the component is resized.
22762              * @param {Roo.Component} this
22763              * @param {Number} adjWidth The box-adjusted width that was set
22764              * @param {Number} adjHeight The box-adjusted height that was set
22765              * @param {Number} rawWidth The width that was originally specified
22766              * @param {Number} rawHeight The height that was originally specified
22767              */
22768         resize : true,
22769         /**
22770          * @event move
22771          * Fires after the component is moved.
22772              * @param {Roo.Component} this
22773              * @param {Number} x The new x position
22774              * @param {Number} y The new y position
22775              */
22776         move : true
22777     });
22778 };
22779
22780 Roo.extend(Roo.BoxComponent, Roo.Component, {
22781     // private, set in afterRender to signify that the component has been rendered
22782     boxReady : false,
22783     // private, used to defer height settings to subclasses
22784     deferHeight: false,
22785     /** @cfg {Number} width
22786      * width (optional) size of component
22787      */
22788      /** @cfg {Number} height
22789      * height (optional) size of component
22790      */
22791      
22792     /**
22793      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22794      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22795      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22796      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22797      * @return {Roo.BoxComponent} this
22798      */
22799     setSize : function(w, h){
22800         // support for standard size objects
22801         if(typeof w == 'object'){
22802             h = w.height;
22803             w = w.width;
22804         }
22805         // not rendered
22806         if(!this.boxReady){
22807             this.width = w;
22808             this.height = h;
22809             return this;
22810         }
22811
22812         // prevent recalcs when not needed
22813         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22814             return this;
22815         }
22816         this.lastSize = {width: w, height: h};
22817
22818         var adj = this.adjustSize(w, h);
22819         var aw = adj.width, ah = adj.height;
22820         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22821             var rz = this.getResizeEl();
22822             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22823                 rz.setSize(aw, ah);
22824             }else if(!this.deferHeight && ah !== undefined){
22825                 rz.setHeight(ah);
22826             }else if(aw !== undefined){
22827                 rz.setWidth(aw);
22828             }
22829             this.onResize(aw, ah, w, h);
22830             this.fireEvent('resize', this, aw, ah, w, h);
22831         }
22832         return this;
22833     },
22834
22835     /**
22836      * Gets the current size of the component's underlying element.
22837      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22838      */
22839     getSize : function(){
22840         return this.el.getSize();
22841     },
22842
22843     /**
22844      * Gets the current XY position of the component's underlying element.
22845      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22846      * @return {Array} The XY position of the element (e.g., [100, 200])
22847      */
22848     getPosition : function(local){
22849         if(local === true){
22850             return [this.el.getLeft(true), this.el.getTop(true)];
22851         }
22852         return this.xy || this.el.getXY();
22853     },
22854
22855     /**
22856      * Gets the current box measurements of the component's underlying element.
22857      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22858      * @returns {Object} box An object in the format {x, y, width, height}
22859      */
22860     getBox : function(local){
22861         var s = this.el.getSize();
22862         if(local){
22863             s.x = this.el.getLeft(true);
22864             s.y = this.el.getTop(true);
22865         }else{
22866             var xy = this.xy || this.el.getXY();
22867             s.x = xy[0];
22868             s.y = xy[1];
22869         }
22870         return s;
22871     },
22872
22873     /**
22874      * Sets the current box measurements of the component's underlying element.
22875      * @param {Object} box An object in the format {x, y, width, height}
22876      * @returns {Roo.BoxComponent} this
22877      */
22878     updateBox : function(box){
22879         this.setSize(box.width, box.height);
22880         this.setPagePosition(box.x, box.y);
22881         return this;
22882     },
22883
22884     // protected
22885     getResizeEl : function(){
22886         return this.resizeEl || this.el;
22887     },
22888
22889     // protected
22890     getPositionEl : function(){
22891         return this.positionEl || this.el;
22892     },
22893
22894     /**
22895      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22896      * This method fires the move event.
22897      * @param {Number} left The new left
22898      * @param {Number} top The new top
22899      * @returns {Roo.BoxComponent} this
22900      */
22901     setPosition : function(x, y){
22902         this.x = x;
22903         this.y = y;
22904         if(!this.boxReady){
22905             return this;
22906         }
22907         var adj = this.adjustPosition(x, y);
22908         var ax = adj.x, ay = adj.y;
22909
22910         var el = this.getPositionEl();
22911         if(ax !== undefined || ay !== undefined){
22912             if(ax !== undefined && ay !== undefined){
22913                 el.setLeftTop(ax, ay);
22914             }else if(ax !== undefined){
22915                 el.setLeft(ax);
22916             }else if(ay !== undefined){
22917                 el.setTop(ay);
22918             }
22919             this.onPosition(ax, ay);
22920             this.fireEvent('move', this, ax, ay);
22921         }
22922         return this;
22923     },
22924
22925     /**
22926      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22927      * This method fires the move event.
22928      * @param {Number} x The new x position
22929      * @param {Number} y The new y position
22930      * @returns {Roo.BoxComponent} this
22931      */
22932     setPagePosition : function(x, y){
22933         this.pageX = x;
22934         this.pageY = y;
22935         if(!this.boxReady){
22936             return;
22937         }
22938         if(x === undefined || y === undefined){ // cannot translate undefined points
22939             return;
22940         }
22941         var p = this.el.translatePoints(x, y);
22942         this.setPosition(p.left, p.top);
22943         return this;
22944     },
22945
22946     // private
22947     onRender : function(ct, position){
22948         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22949         if(this.resizeEl){
22950             this.resizeEl = Roo.get(this.resizeEl);
22951         }
22952         if(this.positionEl){
22953             this.positionEl = Roo.get(this.positionEl);
22954         }
22955     },
22956
22957     // private
22958     afterRender : function(){
22959         Roo.BoxComponent.superclass.afterRender.call(this);
22960         this.boxReady = true;
22961         this.setSize(this.width, this.height);
22962         if(this.x || this.y){
22963             this.setPosition(this.x, this.y);
22964         }
22965         if(this.pageX || this.pageY){
22966             this.setPagePosition(this.pageX, this.pageY);
22967         }
22968     },
22969
22970     /**
22971      * Force the component's size to recalculate based on the underlying element's current height and width.
22972      * @returns {Roo.BoxComponent} this
22973      */
22974     syncSize : function(){
22975         delete this.lastSize;
22976         this.setSize(this.el.getWidth(), this.el.getHeight());
22977         return this;
22978     },
22979
22980     /**
22981      * Called after the component is resized, this method is empty by default but can be implemented by any
22982      * subclass that needs to perform custom logic after a resize occurs.
22983      * @param {Number} adjWidth The box-adjusted width that was set
22984      * @param {Number} adjHeight The box-adjusted height that was set
22985      * @param {Number} rawWidth The width that was originally specified
22986      * @param {Number} rawHeight The height that was originally specified
22987      */
22988     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22989
22990     },
22991
22992     /**
22993      * Called after the component is moved, this method is empty by default but can be implemented by any
22994      * subclass that needs to perform custom logic after a move occurs.
22995      * @param {Number} x The new x position
22996      * @param {Number} y The new y position
22997      */
22998     onPosition : function(x, y){
22999
23000     },
23001
23002     // private
23003     adjustSize : function(w, h){
23004         if(this.autoWidth){
23005             w = 'auto';
23006         }
23007         if(this.autoHeight){
23008             h = 'auto';
23009         }
23010         return {width : w, height: h};
23011     },
23012
23013     // private
23014     adjustPosition : function(x, y){
23015         return {x : x, y: y};
23016     }
23017 });/*
23018  * Based on:
23019  * Ext JS Library 1.1.1
23020  * Copyright(c) 2006-2007, Ext JS, LLC.
23021  *
23022  * Originally Released Under LGPL - original licence link has changed is not relivant.
23023  *
23024  * Fork - LGPL
23025  * <script type="text/javascript">
23026  */
23027
23028
23029 /**
23030  * @class Roo.SplitBar
23031  * @extends Roo.util.Observable
23032  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23033  * <br><br>
23034  * Usage:
23035  * <pre><code>
23036 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23037                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23038 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23039 split.minSize = 100;
23040 split.maxSize = 600;
23041 split.animate = true;
23042 split.on('moved', splitterMoved);
23043 </code></pre>
23044  * @constructor
23045  * Create a new SplitBar
23046  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23047  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23048  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23049  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23050                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23051                         position of the SplitBar).
23052  */
23053 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23054     
23055     /** @private */
23056     this.el = Roo.get(dragElement, true);
23057     this.el.dom.unselectable = "on";
23058     /** @private */
23059     this.resizingEl = Roo.get(resizingElement, true);
23060
23061     /**
23062      * @private
23063      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23064      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23065      * @type Number
23066      */
23067     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23068     
23069     /**
23070      * The minimum size of the resizing element. (Defaults to 0)
23071      * @type Number
23072      */
23073     this.minSize = 0;
23074     
23075     /**
23076      * The maximum size of the resizing element. (Defaults to 2000)
23077      * @type Number
23078      */
23079     this.maxSize = 2000;
23080     
23081     /**
23082      * Whether to animate the transition to the new size
23083      * @type Boolean
23084      */
23085     this.animate = false;
23086     
23087     /**
23088      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23089      * @type Boolean
23090      */
23091     this.useShim = false;
23092     
23093     /** @private */
23094     this.shim = null;
23095     
23096     if(!existingProxy){
23097         /** @private */
23098         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23099     }else{
23100         this.proxy = Roo.get(existingProxy).dom;
23101     }
23102     /** @private */
23103     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23104     
23105     /** @private */
23106     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23107     
23108     /** @private */
23109     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23110     
23111     /** @private */
23112     this.dragSpecs = {};
23113     
23114     /**
23115      * @private The adapter to use to positon and resize elements
23116      */
23117     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23118     this.adapter.init(this);
23119     
23120     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23121         /** @private */
23122         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23123         this.el.addClass("x-splitbar-h");
23124     }else{
23125         /** @private */
23126         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23127         this.el.addClass("x-splitbar-v");
23128     }
23129     
23130     this.addEvents({
23131         /**
23132          * @event resize
23133          * Fires when the splitter is moved (alias for {@link #event-moved})
23134          * @param {Roo.SplitBar} this
23135          * @param {Number} newSize the new width or height
23136          */
23137         "resize" : true,
23138         /**
23139          * @event moved
23140          * Fires when the splitter is moved
23141          * @param {Roo.SplitBar} this
23142          * @param {Number} newSize the new width or height
23143          */
23144         "moved" : true,
23145         /**
23146          * @event beforeresize
23147          * Fires before the splitter is dragged
23148          * @param {Roo.SplitBar} this
23149          */
23150         "beforeresize" : true,
23151
23152         "beforeapply" : true
23153     });
23154
23155     Roo.util.Observable.call(this);
23156 };
23157
23158 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23159     onStartProxyDrag : function(x, y){
23160         this.fireEvent("beforeresize", this);
23161         if(!this.overlay){
23162             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23163             o.unselectable();
23164             o.enableDisplayMode("block");
23165             // all splitbars share the same overlay
23166             Roo.SplitBar.prototype.overlay = o;
23167         }
23168         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23169         this.overlay.show();
23170         Roo.get(this.proxy).setDisplayed("block");
23171         var size = this.adapter.getElementSize(this);
23172         this.activeMinSize = this.getMinimumSize();;
23173         this.activeMaxSize = this.getMaximumSize();;
23174         var c1 = size - this.activeMinSize;
23175         var c2 = Math.max(this.activeMaxSize - size, 0);
23176         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23177             this.dd.resetConstraints();
23178             this.dd.setXConstraint(
23179                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23180                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23181             );
23182             this.dd.setYConstraint(0, 0);
23183         }else{
23184             this.dd.resetConstraints();
23185             this.dd.setXConstraint(0, 0);
23186             this.dd.setYConstraint(
23187                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23188                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23189             );
23190          }
23191         this.dragSpecs.startSize = size;
23192         this.dragSpecs.startPoint = [x, y];
23193         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23194     },
23195     
23196     /** 
23197      * @private Called after the drag operation by the DDProxy
23198      */
23199     onEndProxyDrag : function(e){
23200         Roo.get(this.proxy).setDisplayed(false);
23201         var endPoint = Roo.lib.Event.getXY(e);
23202         if(this.overlay){
23203             this.overlay.hide();
23204         }
23205         var newSize;
23206         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23207             newSize = this.dragSpecs.startSize + 
23208                 (this.placement == Roo.SplitBar.LEFT ?
23209                     endPoint[0] - this.dragSpecs.startPoint[0] :
23210                     this.dragSpecs.startPoint[0] - endPoint[0]
23211                 );
23212         }else{
23213             newSize = this.dragSpecs.startSize + 
23214                 (this.placement == Roo.SplitBar.TOP ?
23215                     endPoint[1] - this.dragSpecs.startPoint[1] :
23216                     this.dragSpecs.startPoint[1] - endPoint[1]
23217                 );
23218         }
23219         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23220         if(newSize != this.dragSpecs.startSize){
23221             if(this.fireEvent('beforeapply', this, newSize) !== false){
23222                 this.adapter.setElementSize(this, newSize);
23223                 this.fireEvent("moved", this, newSize);
23224                 this.fireEvent("resize", this, newSize);
23225             }
23226         }
23227     },
23228     
23229     /**
23230      * Get the adapter this SplitBar uses
23231      * @return The adapter object
23232      */
23233     getAdapter : function(){
23234         return this.adapter;
23235     },
23236     
23237     /**
23238      * Set the adapter this SplitBar uses
23239      * @param {Object} adapter A SplitBar adapter object
23240      */
23241     setAdapter : function(adapter){
23242         this.adapter = adapter;
23243         this.adapter.init(this);
23244     },
23245     
23246     /**
23247      * Gets the minimum size for the resizing element
23248      * @return {Number} The minimum size
23249      */
23250     getMinimumSize : function(){
23251         return this.minSize;
23252     },
23253     
23254     /**
23255      * Sets the minimum size for the resizing element
23256      * @param {Number} minSize The minimum size
23257      */
23258     setMinimumSize : function(minSize){
23259         this.minSize = minSize;
23260     },
23261     
23262     /**
23263      * Gets the maximum size for the resizing element
23264      * @return {Number} The maximum size
23265      */
23266     getMaximumSize : function(){
23267         return this.maxSize;
23268     },
23269     
23270     /**
23271      * Sets the maximum size for the resizing element
23272      * @param {Number} maxSize The maximum size
23273      */
23274     setMaximumSize : function(maxSize){
23275         this.maxSize = maxSize;
23276     },
23277     
23278     /**
23279      * Sets the initialize size for the resizing element
23280      * @param {Number} size The initial size
23281      */
23282     setCurrentSize : function(size){
23283         var oldAnimate = this.animate;
23284         this.animate = false;
23285         this.adapter.setElementSize(this, size);
23286         this.animate = oldAnimate;
23287     },
23288     
23289     /**
23290      * Destroy this splitbar. 
23291      * @param {Boolean} removeEl True to remove the element
23292      */
23293     destroy : function(removeEl){
23294         if(this.shim){
23295             this.shim.remove();
23296         }
23297         this.dd.unreg();
23298         this.proxy.parentNode.removeChild(this.proxy);
23299         if(removeEl){
23300             this.el.remove();
23301         }
23302     }
23303 });
23304
23305 /**
23306  * @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.
23307  */
23308 Roo.SplitBar.createProxy = function(dir){
23309     var proxy = new Roo.Element(document.createElement("div"));
23310     proxy.unselectable();
23311     var cls = 'x-splitbar-proxy';
23312     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23313     document.body.appendChild(proxy.dom);
23314     return proxy.dom;
23315 };
23316
23317 /** 
23318  * @class Roo.SplitBar.BasicLayoutAdapter
23319  * Default Adapter. It assumes the splitter and resizing element are not positioned
23320  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23321  */
23322 Roo.SplitBar.BasicLayoutAdapter = function(){
23323 };
23324
23325 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23326     // do nothing for now
23327     init : function(s){
23328     
23329     },
23330     /**
23331      * Called before drag operations to get the current size of the resizing element. 
23332      * @param {Roo.SplitBar} s The SplitBar using this adapter
23333      */
23334      getElementSize : function(s){
23335         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23336             return s.resizingEl.getWidth();
23337         }else{
23338             return s.resizingEl.getHeight();
23339         }
23340     },
23341     
23342     /**
23343      * Called after drag operations to set the size of the resizing element.
23344      * @param {Roo.SplitBar} s The SplitBar using this adapter
23345      * @param {Number} newSize The new size to set
23346      * @param {Function} onComplete A function to be invoked when resizing is complete
23347      */
23348     setElementSize : function(s, newSize, onComplete){
23349         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23350             if(!s.animate){
23351                 s.resizingEl.setWidth(newSize);
23352                 if(onComplete){
23353                     onComplete(s, newSize);
23354                 }
23355             }else{
23356                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23357             }
23358         }else{
23359             
23360             if(!s.animate){
23361                 s.resizingEl.setHeight(newSize);
23362                 if(onComplete){
23363                     onComplete(s, newSize);
23364                 }
23365             }else{
23366                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23367             }
23368         }
23369     }
23370 };
23371
23372 /** 
23373  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23374  * @extends Roo.SplitBar.BasicLayoutAdapter
23375  * Adapter that  moves the splitter element to align with the resized sizing element. 
23376  * Used with an absolute positioned SplitBar.
23377  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23378  * document.body, make sure you assign an id to the body element.
23379  */
23380 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23381     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23382     this.container = Roo.get(container);
23383 };
23384
23385 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23386     init : function(s){
23387         this.basic.init(s);
23388     },
23389     
23390     getElementSize : function(s){
23391         return this.basic.getElementSize(s);
23392     },
23393     
23394     setElementSize : function(s, newSize, onComplete){
23395         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23396     },
23397     
23398     moveSplitter : function(s){
23399         var yes = Roo.SplitBar;
23400         switch(s.placement){
23401             case yes.LEFT:
23402                 s.el.setX(s.resizingEl.getRight());
23403                 break;
23404             case yes.RIGHT:
23405                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23406                 break;
23407             case yes.TOP:
23408                 s.el.setY(s.resizingEl.getBottom());
23409                 break;
23410             case yes.BOTTOM:
23411                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23412                 break;
23413         }
23414     }
23415 };
23416
23417 /**
23418  * Orientation constant - Create a vertical SplitBar
23419  * @static
23420  * @type Number
23421  */
23422 Roo.SplitBar.VERTICAL = 1;
23423
23424 /**
23425  * Orientation constant - Create a horizontal SplitBar
23426  * @static
23427  * @type Number
23428  */
23429 Roo.SplitBar.HORIZONTAL = 2;
23430
23431 /**
23432  * Placement constant - The resizing element is to the left of the splitter element
23433  * @static
23434  * @type Number
23435  */
23436 Roo.SplitBar.LEFT = 1;
23437
23438 /**
23439  * Placement constant - The resizing element is to the right of the splitter element
23440  * @static
23441  * @type Number
23442  */
23443 Roo.SplitBar.RIGHT = 2;
23444
23445 /**
23446  * Placement constant - The resizing element is positioned above the splitter element
23447  * @static
23448  * @type Number
23449  */
23450 Roo.SplitBar.TOP = 3;
23451
23452 /**
23453  * Placement constant - The resizing element is positioned under splitter element
23454  * @static
23455  * @type Number
23456  */
23457 Roo.SplitBar.BOTTOM = 4;
23458 /*
23459  * Based on:
23460  * Ext JS Library 1.1.1
23461  * Copyright(c) 2006-2007, Ext JS, LLC.
23462  *
23463  * Originally Released Under LGPL - original licence link has changed is not relivant.
23464  *
23465  * Fork - LGPL
23466  * <script type="text/javascript">
23467  */
23468
23469 /**
23470  * @class Roo.View
23471  * @extends Roo.util.Observable
23472  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23473  * This class also supports single and multi selection modes. <br>
23474  * Create a data model bound view:
23475  <pre><code>
23476  var store = new Roo.data.Store(...);
23477
23478  var view = new Roo.View({
23479     el : "my-element",
23480     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23481  
23482     singleSelect: true,
23483     selectedClass: "ydataview-selected",
23484     store: store
23485  });
23486
23487  // listen for node click?
23488  view.on("click", function(vw, index, node, e){
23489  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23490  });
23491
23492  // load XML data
23493  dataModel.load("foobar.xml");
23494  </code></pre>
23495  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23496  * <br><br>
23497  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23498  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23499  * 
23500  * Note: old style constructor is still suported (container, template, config)
23501  * 
23502  * @constructor
23503  * Create a new View
23504  * @param {Object} config The config object
23505  * 
23506  */
23507 Roo.View = function(config, depreciated_tpl, depreciated_config){
23508     
23509     if (typeof(depreciated_tpl) == 'undefined') {
23510         // new way.. - universal constructor.
23511         Roo.apply(this, config);
23512         this.el  = Roo.get(this.el);
23513     } else {
23514         // old format..
23515         this.el  = Roo.get(config);
23516         this.tpl = depreciated_tpl;
23517         Roo.apply(this, depreciated_config);
23518     }
23519      
23520     
23521     if(typeof(this.tpl) == "string"){
23522         this.tpl = new Roo.Template(this.tpl);
23523     } else {
23524         // support xtype ctors..
23525         this.tpl = new Roo.factory(this.tpl, Roo);
23526     }
23527     
23528     
23529     this.tpl.compile();
23530    
23531
23532      
23533     /** @private */
23534     this.addEvents({
23535     /**
23536      * @event beforeclick
23537      * Fires before a click is processed. Returns false to cancel the default action.
23538      * @param {Roo.View} this
23539      * @param {Number} index The index of the target node
23540      * @param {HTMLElement} node The target node
23541      * @param {Roo.EventObject} e The raw event object
23542      */
23543         "beforeclick" : true,
23544     /**
23545      * @event click
23546      * Fires when a template node is clicked.
23547      * @param {Roo.View} this
23548      * @param {Number} index The index of the target node
23549      * @param {HTMLElement} node The target node
23550      * @param {Roo.EventObject} e The raw event object
23551      */
23552         "click" : true,
23553     /**
23554      * @event dblclick
23555      * Fires when a template node is double clicked.
23556      * @param {Roo.View} this
23557      * @param {Number} index The index of the target node
23558      * @param {HTMLElement} node The target node
23559      * @param {Roo.EventObject} e The raw event object
23560      */
23561         "dblclick" : true,
23562     /**
23563      * @event contextmenu
23564      * Fires when a template node is right clicked.
23565      * @param {Roo.View} this
23566      * @param {Number} index The index of the target node
23567      * @param {HTMLElement} node The target node
23568      * @param {Roo.EventObject} e The raw event object
23569      */
23570         "contextmenu" : true,
23571     /**
23572      * @event selectionchange
23573      * Fires when the selected nodes change.
23574      * @param {Roo.View} this
23575      * @param {Array} selections Array of the selected nodes
23576      */
23577         "selectionchange" : true,
23578
23579     /**
23580      * @event beforeselect
23581      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23582      * @param {Roo.View} this
23583      * @param {HTMLElement} node The node to be selected
23584      * @param {Array} selections Array of currently selected nodes
23585      */
23586         "beforeselect" : true
23587     });
23588
23589     this.el.on({
23590         "click": this.onClick,
23591         "dblclick": this.onDblClick,
23592         "contextmenu": this.onContextMenu,
23593         scope:this
23594     });
23595
23596     this.selections = [];
23597     this.nodes = [];
23598     this.cmp = new Roo.CompositeElementLite([]);
23599     if(this.store){
23600         this.store = Roo.factory(this.store, Roo.data);
23601         this.setStore(this.store, true);
23602     }
23603     Roo.View.superclass.constructor.call(this);
23604 };
23605
23606 Roo.extend(Roo.View, Roo.util.Observable, {
23607     
23608      /**
23609      * @cfg {Roo.data.Store} store Data store to load data from.
23610      */
23611     store : false,
23612     
23613     /**
23614      * @cfg {String|Roo.Element} el The container element.
23615      */
23616     el : '',
23617     
23618     /**
23619      * @cfg {String|Roo.Template} tpl The template used by this View 
23620      */
23621     tpl : false,
23622     
23623     /**
23624      * @cfg {String} selectedClass The css class to add to selected nodes
23625      */
23626     selectedClass : "x-view-selected",
23627      /**
23628      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23629      */
23630     emptyText : "",
23631     /**
23632      * @cfg {Boolean} multiSelect Allow multiple selection
23633      */
23634     
23635     multiSelect : false,
23636     /**
23637      * @cfg {Boolean} singleSelect Allow single selection
23638      */
23639     singleSelect:  false,
23640     
23641     /**
23642      * Returns the element this view is bound to.
23643      * @return {Roo.Element}
23644      */
23645     getEl : function(){
23646         return this.el;
23647     },
23648
23649     /**
23650      * Refreshes the view.
23651      */
23652     refresh : function(){
23653         var t = this.tpl;
23654         this.clearSelections();
23655         this.el.update("");
23656         var html = [];
23657         var records = this.store.getRange();
23658         if(records.length < 1){
23659             this.el.update(this.emptyText);
23660             return;
23661         }
23662         for(var i = 0, len = records.length; i < len; i++){
23663             var data = this.prepareData(records[i].data, i, records[i]);
23664             html[html.length] = t.apply(data);
23665         }
23666         this.el.update(html.join(""));
23667         this.nodes = this.el.dom.childNodes;
23668         this.updateIndexes(0);
23669     },
23670
23671     /**
23672      * Function to override to reformat the data that is sent to
23673      * the template for each node.
23674      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23675      * a JSON object for an UpdateManager bound view).
23676      */
23677     prepareData : function(data){
23678         return data;
23679     },
23680
23681     onUpdate : function(ds, record){
23682         this.clearSelections();
23683         var index = this.store.indexOf(record);
23684         var n = this.nodes[index];
23685         this.tpl.insertBefore(n, this.prepareData(record.data));
23686         n.parentNode.removeChild(n);
23687         this.updateIndexes(index, index);
23688     },
23689
23690     onAdd : function(ds, records, index){
23691         this.clearSelections();
23692         if(this.nodes.length == 0){
23693             this.refresh();
23694             return;
23695         }
23696         var n = this.nodes[index];
23697         for(var i = 0, len = records.length; i < len; i++){
23698             var d = this.prepareData(records[i].data);
23699             if(n){
23700                 this.tpl.insertBefore(n, d);
23701             }else{
23702                 this.tpl.append(this.el, d);
23703             }
23704         }
23705         this.updateIndexes(index);
23706     },
23707
23708     onRemove : function(ds, record, index){
23709         this.clearSelections();
23710         this.el.dom.removeChild(this.nodes[index]);
23711         this.updateIndexes(index);
23712     },
23713
23714     /**
23715      * Refresh an individual node.
23716      * @param {Number} index
23717      */
23718     refreshNode : function(index){
23719         this.onUpdate(this.store, this.store.getAt(index));
23720     },
23721
23722     updateIndexes : function(startIndex, endIndex){
23723         var ns = this.nodes;
23724         startIndex = startIndex || 0;
23725         endIndex = endIndex || ns.length - 1;
23726         for(var i = startIndex; i <= endIndex; i++){
23727             ns[i].nodeIndex = i;
23728         }
23729     },
23730
23731     /**
23732      * Changes the data store this view uses and refresh the view.
23733      * @param {Store} store
23734      */
23735     setStore : function(store, initial){
23736         if(!initial && this.store){
23737             this.store.un("datachanged", this.refresh);
23738             this.store.un("add", this.onAdd);
23739             this.store.un("remove", this.onRemove);
23740             this.store.un("update", this.onUpdate);
23741             this.store.un("clear", this.refresh);
23742         }
23743         if(store){
23744           
23745             store.on("datachanged", this.refresh, this);
23746             store.on("add", this.onAdd, this);
23747             store.on("remove", this.onRemove, this);
23748             store.on("update", this.onUpdate, this);
23749             store.on("clear", this.refresh, this);
23750         }
23751         
23752         if(store){
23753             this.refresh();
23754         }
23755     },
23756
23757     /**
23758      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23759      * @param {HTMLElement} node
23760      * @return {HTMLElement} The template node
23761      */
23762     findItemFromChild : function(node){
23763         var el = this.el.dom;
23764         if(!node || node.parentNode == el){
23765                     return node;
23766             }
23767             var p = node.parentNode;
23768             while(p && p != el){
23769             if(p.parentNode == el){
23770                 return p;
23771             }
23772             p = p.parentNode;
23773         }
23774             return null;
23775     },
23776
23777     /** @ignore */
23778     onClick : function(e){
23779         var item = this.findItemFromChild(e.getTarget());
23780         if(item){
23781             var index = this.indexOf(item);
23782             if(this.onItemClick(item, index, e) !== false){
23783                 this.fireEvent("click", this, index, item, e);
23784             }
23785         }else{
23786             this.clearSelections();
23787         }
23788     },
23789
23790     /** @ignore */
23791     onContextMenu : function(e){
23792         var item = this.findItemFromChild(e.getTarget());
23793         if(item){
23794             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23795         }
23796     },
23797
23798     /** @ignore */
23799     onDblClick : function(e){
23800         var item = this.findItemFromChild(e.getTarget());
23801         if(item){
23802             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23803         }
23804     },
23805
23806     onItemClick : function(item, index, e){
23807         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23808             return false;
23809         }
23810         if(this.multiSelect || this.singleSelect){
23811             if(this.multiSelect && e.shiftKey && this.lastSelection){
23812                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23813             }else{
23814                 this.select(item, this.multiSelect && e.ctrlKey);
23815                 this.lastSelection = item;
23816             }
23817             e.preventDefault();
23818         }
23819         return true;
23820     },
23821
23822     /**
23823      * Get the number of selected nodes.
23824      * @return {Number}
23825      */
23826     getSelectionCount : function(){
23827         return this.selections.length;
23828     },
23829
23830     /**
23831      * Get the currently selected nodes.
23832      * @return {Array} An array of HTMLElements
23833      */
23834     getSelectedNodes : function(){
23835         return this.selections;
23836     },
23837
23838     /**
23839      * Get the indexes of the selected nodes.
23840      * @return {Array}
23841      */
23842     getSelectedIndexes : function(){
23843         var indexes = [], s = this.selections;
23844         for(var i = 0, len = s.length; i < len; i++){
23845             indexes.push(s[i].nodeIndex);
23846         }
23847         return indexes;
23848     },
23849
23850     /**
23851      * Clear all selections
23852      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23853      */
23854     clearSelections : function(suppressEvent){
23855         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23856             this.cmp.elements = this.selections;
23857             this.cmp.removeClass(this.selectedClass);
23858             this.selections = [];
23859             if(!suppressEvent){
23860                 this.fireEvent("selectionchange", this, this.selections);
23861             }
23862         }
23863     },
23864
23865     /**
23866      * Returns true if the passed node is selected
23867      * @param {HTMLElement/Number} node The node or node index
23868      * @return {Boolean}
23869      */
23870     isSelected : function(node){
23871         var s = this.selections;
23872         if(s.length < 1){
23873             return false;
23874         }
23875         node = this.getNode(node);
23876         return s.indexOf(node) !== -1;
23877     },
23878
23879     /**
23880      * Selects nodes.
23881      * @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
23882      * @param {Boolean} keepExisting (optional) true to keep existing selections
23883      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23884      */
23885     select : function(nodeInfo, keepExisting, suppressEvent){
23886         if(nodeInfo instanceof Array){
23887             if(!keepExisting){
23888                 this.clearSelections(true);
23889             }
23890             for(var i = 0, len = nodeInfo.length; i < len; i++){
23891                 this.select(nodeInfo[i], true, true);
23892             }
23893         } else{
23894             var node = this.getNode(nodeInfo);
23895             if(node && !this.isSelected(node)){
23896                 if(!keepExisting){
23897                     this.clearSelections(true);
23898                 }
23899                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23900                     Roo.fly(node).addClass(this.selectedClass);
23901                     this.selections.push(node);
23902                     if(!suppressEvent){
23903                         this.fireEvent("selectionchange", this, this.selections);
23904                     }
23905                 }
23906             }
23907         }
23908     },
23909
23910     /**
23911      * Gets a template node.
23912      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23913      * @return {HTMLElement} The node or null if it wasn't found
23914      */
23915     getNode : function(nodeInfo){
23916         if(typeof nodeInfo == "string"){
23917             return document.getElementById(nodeInfo);
23918         }else if(typeof nodeInfo == "number"){
23919             return this.nodes[nodeInfo];
23920         }
23921         return nodeInfo;
23922     },
23923
23924     /**
23925      * Gets a range template nodes.
23926      * @param {Number} startIndex
23927      * @param {Number} endIndex
23928      * @return {Array} An array of nodes
23929      */
23930     getNodes : function(start, end){
23931         var ns = this.nodes;
23932         start = start || 0;
23933         end = typeof end == "undefined" ? ns.length - 1 : end;
23934         var nodes = [];
23935         if(start <= end){
23936             for(var i = start; i <= end; i++){
23937                 nodes.push(ns[i]);
23938             }
23939         } else{
23940             for(var i = start; i >= end; i--){
23941                 nodes.push(ns[i]);
23942             }
23943         }
23944         return nodes;
23945     },
23946
23947     /**
23948      * Finds the index of the passed node
23949      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23950      * @return {Number} The index of the node or -1
23951      */
23952     indexOf : function(node){
23953         node = this.getNode(node);
23954         if(typeof node.nodeIndex == "number"){
23955             return node.nodeIndex;
23956         }
23957         var ns = this.nodes;
23958         for(var i = 0, len = ns.length; i < len; i++){
23959             if(ns[i] == node){
23960                 return i;
23961             }
23962         }
23963         return -1;
23964     }
23965 });
23966 /*
23967  * Based on:
23968  * Ext JS Library 1.1.1
23969  * Copyright(c) 2006-2007, Ext JS, LLC.
23970  *
23971  * Originally Released Under LGPL - original licence link has changed is not relivant.
23972  *
23973  * Fork - LGPL
23974  * <script type="text/javascript">
23975  */
23976
23977 /**
23978  * @class Roo.JsonView
23979  * @extends Roo.View
23980  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23981 <pre><code>
23982 var view = new Roo.JsonView({
23983     container: "my-element",
23984     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23985     multiSelect: true, 
23986     jsonRoot: "data" 
23987 });
23988
23989 // listen for node click?
23990 view.on("click", function(vw, index, node, e){
23991     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23992 });
23993
23994 // direct load of JSON data
23995 view.load("foobar.php");
23996
23997 // Example from my blog list
23998 var tpl = new Roo.Template(
23999     '&lt;div class="entry"&gt;' +
24000     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24001     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24002     "&lt;/div&gt;&lt;hr /&gt;"
24003 );
24004
24005 var moreView = new Roo.JsonView({
24006     container :  "entry-list", 
24007     template : tpl,
24008     jsonRoot: "posts"
24009 });
24010 moreView.on("beforerender", this.sortEntries, this);
24011 moreView.load({
24012     url: "/blog/get-posts.php",
24013     params: "allposts=true",
24014     text: "Loading Blog Entries..."
24015 });
24016 </code></pre>
24017
24018 * Note: old code is supported with arguments : (container, template, config)
24019
24020
24021  * @constructor
24022  * Create a new JsonView
24023  * 
24024  * @param {Object} config The config object
24025  * 
24026  */
24027 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24028     
24029     
24030     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24031
24032     var um = this.el.getUpdateManager();
24033     um.setRenderer(this);
24034     um.on("update", this.onLoad, this);
24035     um.on("failure", this.onLoadException, this);
24036
24037     /**
24038      * @event beforerender
24039      * Fires before rendering of the downloaded JSON data.
24040      * @param {Roo.JsonView} this
24041      * @param {Object} data The JSON data loaded
24042      */
24043     /**
24044      * @event load
24045      * Fires when data is loaded.
24046      * @param {Roo.JsonView} this
24047      * @param {Object} data The JSON data loaded
24048      * @param {Object} response The raw Connect response object
24049      */
24050     /**
24051      * @event loadexception
24052      * Fires when loading fails.
24053      * @param {Roo.JsonView} this
24054      * @param {Object} response The raw Connect response object
24055      */
24056     this.addEvents({
24057         'beforerender' : true,
24058         'load' : true,
24059         'loadexception' : true
24060     });
24061 };
24062 Roo.extend(Roo.JsonView, Roo.View, {
24063     /**
24064      * @type {String} The root property in the loaded JSON object that contains the data
24065      */
24066     jsonRoot : "",
24067
24068     /**
24069      * Refreshes the view.
24070      */
24071     refresh : function(){
24072         this.clearSelections();
24073         this.el.update("");
24074         var html = [];
24075         var o = this.jsonData;
24076         if(o && o.length > 0){
24077             for(var i = 0, len = o.length; i < len; i++){
24078                 var data = this.prepareData(o[i], i, o);
24079                 html[html.length] = this.tpl.apply(data);
24080             }
24081         }else{
24082             html.push(this.emptyText);
24083         }
24084         this.el.update(html.join(""));
24085         this.nodes = this.el.dom.childNodes;
24086         this.updateIndexes(0);
24087     },
24088
24089     /**
24090      * 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.
24091      * @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:
24092      <pre><code>
24093      view.load({
24094          url: "your-url.php",
24095          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24096          callback: yourFunction,
24097          scope: yourObject, //(optional scope)
24098          discardUrl: false,
24099          nocache: false,
24100          text: "Loading...",
24101          timeout: 30,
24102          scripts: false
24103      });
24104      </code></pre>
24105      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24106      * 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.
24107      * @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}
24108      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24109      * @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.
24110      */
24111     load : function(){
24112         var um = this.el.getUpdateManager();
24113         um.update.apply(um, arguments);
24114     },
24115
24116     render : function(el, response){
24117         this.clearSelections();
24118         this.el.update("");
24119         var o;
24120         try{
24121             o = Roo.util.JSON.decode(response.responseText);
24122             if(this.jsonRoot){
24123                 
24124                 o = o[this.jsonRoot];
24125             }
24126         } catch(e){
24127         }
24128         /**
24129          * The current JSON data or null
24130          */
24131         this.jsonData = o;
24132         this.beforeRender();
24133         this.refresh();
24134     },
24135
24136 /**
24137  * Get the number of records in the current JSON dataset
24138  * @return {Number}
24139  */
24140     getCount : function(){
24141         return this.jsonData ? this.jsonData.length : 0;
24142     },
24143
24144 /**
24145  * Returns the JSON object for the specified node(s)
24146  * @param {HTMLElement/Array} node The node or an array of nodes
24147  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24148  * you get the JSON object for the node
24149  */
24150     getNodeData : function(node){
24151         if(node instanceof Array){
24152             var data = [];
24153             for(var i = 0, len = node.length; i < len; i++){
24154                 data.push(this.getNodeData(node[i]));
24155             }
24156             return data;
24157         }
24158         return this.jsonData[this.indexOf(node)] || null;
24159     },
24160
24161     beforeRender : function(){
24162         this.snapshot = this.jsonData;
24163         if(this.sortInfo){
24164             this.sort.apply(this, this.sortInfo);
24165         }
24166         this.fireEvent("beforerender", this, this.jsonData);
24167     },
24168
24169     onLoad : function(el, o){
24170         this.fireEvent("load", this, this.jsonData, o);
24171     },
24172
24173     onLoadException : function(el, o){
24174         this.fireEvent("loadexception", this, o);
24175     },
24176
24177 /**
24178  * Filter the data by a specific property.
24179  * @param {String} property A property on your JSON objects
24180  * @param {String/RegExp} value Either string that the property values
24181  * should start with, or a RegExp to test against the property
24182  */
24183     filter : function(property, value){
24184         if(this.jsonData){
24185             var data = [];
24186             var ss = this.snapshot;
24187             if(typeof value == "string"){
24188                 var vlen = value.length;
24189                 if(vlen == 0){
24190                     this.clearFilter();
24191                     return;
24192                 }
24193                 value = value.toLowerCase();
24194                 for(var i = 0, len = ss.length; i < len; i++){
24195                     var o = ss[i];
24196                     if(o[property].substr(0, vlen).toLowerCase() == value){
24197                         data.push(o);
24198                     }
24199                 }
24200             } else if(value.exec){ // regex?
24201                 for(var i = 0, len = ss.length; i < len; i++){
24202                     var o = ss[i];
24203                     if(value.test(o[property])){
24204                         data.push(o);
24205                     }
24206                 }
24207             } else{
24208                 return;
24209             }
24210             this.jsonData = data;
24211             this.refresh();
24212         }
24213     },
24214
24215 /**
24216  * Filter by a function. The passed function will be called with each
24217  * object in the current dataset. If the function returns true the value is kept,
24218  * otherwise it is filtered.
24219  * @param {Function} fn
24220  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24221  */
24222     filterBy : function(fn, scope){
24223         if(this.jsonData){
24224             var data = [];
24225             var ss = this.snapshot;
24226             for(var i = 0, len = ss.length; i < len; i++){
24227                 var o = ss[i];
24228                 if(fn.call(scope || this, o)){
24229                     data.push(o);
24230                 }
24231             }
24232             this.jsonData = data;
24233             this.refresh();
24234         }
24235     },
24236
24237 /**
24238  * Clears the current filter.
24239  */
24240     clearFilter : function(){
24241         if(this.snapshot && this.jsonData != this.snapshot){
24242             this.jsonData = this.snapshot;
24243             this.refresh();
24244         }
24245     },
24246
24247
24248 /**
24249  * Sorts the data for this view and refreshes it.
24250  * @param {String} property A property on your JSON objects to sort on
24251  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24252  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24253  */
24254     sort : function(property, dir, sortType){
24255         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24256         if(this.jsonData){
24257             var p = property;
24258             var dsc = dir && dir.toLowerCase() == "desc";
24259             var f = function(o1, o2){
24260                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24261                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24262                 ;
24263                 if(v1 < v2){
24264                     return dsc ? +1 : -1;
24265                 } else if(v1 > v2){
24266                     return dsc ? -1 : +1;
24267                 } else{
24268                     return 0;
24269                 }
24270             };
24271             this.jsonData.sort(f);
24272             this.refresh();
24273             if(this.jsonData != this.snapshot){
24274                 this.snapshot.sort(f);
24275             }
24276         }
24277     }
24278 });/*
24279  * Based on:
24280  * Ext JS Library 1.1.1
24281  * Copyright(c) 2006-2007, Ext JS, LLC.
24282  *
24283  * Originally Released Under LGPL - original licence link has changed is not relivant.
24284  *
24285  * Fork - LGPL
24286  * <script type="text/javascript">
24287  */
24288  
24289
24290 /**
24291  * @class Roo.ColorPalette
24292  * @extends Roo.Component
24293  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24294  * Here's an example of typical usage:
24295  * <pre><code>
24296 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24297 cp.render('my-div');
24298
24299 cp.on('select', function(palette, selColor){
24300     // do something with selColor
24301 });
24302 </code></pre>
24303  * @constructor
24304  * Create a new ColorPalette
24305  * @param {Object} config The config object
24306  */
24307 Roo.ColorPalette = function(config){
24308     Roo.ColorPalette.superclass.constructor.call(this, config);
24309     this.addEvents({
24310         /**
24311              * @event select
24312              * Fires when a color is selected
24313              * @param {ColorPalette} this
24314              * @param {String} color The 6-digit color hex code (without the # symbol)
24315              */
24316         select: true
24317     });
24318
24319     if(this.handler){
24320         this.on("select", this.handler, this.scope, true);
24321     }
24322 };
24323 Roo.extend(Roo.ColorPalette, Roo.Component, {
24324     /**
24325      * @cfg {String} itemCls
24326      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24327      */
24328     itemCls : "x-color-palette",
24329     /**
24330      * @cfg {String} value
24331      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24332      * the hex codes are case-sensitive.
24333      */
24334     value : null,
24335     clickEvent:'click',
24336     // private
24337     ctype: "Roo.ColorPalette",
24338
24339     /**
24340      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24341      */
24342     allowReselect : false,
24343
24344     /**
24345      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24346      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24347      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24348      * of colors with the width setting until the box is symmetrical.</p>
24349      * <p>You can override individual colors if needed:</p>
24350      * <pre><code>
24351 var cp = new Roo.ColorPalette();
24352 cp.colors[0] = "FF0000";  // change the first box to red
24353 </code></pre>
24354
24355 Or you can provide a custom array of your own for complete control:
24356 <pre><code>
24357 var cp = new Roo.ColorPalette();
24358 cp.colors = ["000000", "993300", "333300"];
24359 </code></pre>
24360      * @type Array
24361      */
24362     colors : [
24363         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24364         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24365         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24366         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24367         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24368     ],
24369
24370     // private
24371     onRender : function(container, position){
24372         var t = new Roo.MasterTemplate(
24373             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24374         );
24375         var c = this.colors;
24376         for(var i = 0, len = c.length; i < len; i++){
24377             t.add([c[i]]);
24378         }
24379         var el = document.createElement("div");
24380         el.className = this.itemCls;
24381         t.overwrite(el);
24382         container.dom.insertBefore(el, position);
24383         this.el = Roo.get(el);
24384         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24385         if(this.clickEvent != 'click'){
24386             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24387         }
24388     },
24389
24390     // private
24391     afterRender : function(){
24392         Roo.ColorPalette.superclass.afterRender.call(this);
24393         if(this.value){
24394             var s = this.value;
24395             this.value = null;
24396             this.select(s);
24397         }
24398     },
24399
24400     // private
24401     handleClick : function(e, t){
24402         e.preventDefault();
24403         if(!this.disabled){
24404             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24405             this.select(c.toUpperCase());
24406         }
24407     },
24408
24409     /**
24410      * Selects the specified color in the palette (fires the select event)
24411      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24412      */
24413     select : function(color){
24414         color = color.replace("#", "");
24415         if(color != this.value || this.allowReselect){
24416             var el = this.el;
24417             if(this.value){
24418                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24419             }
24420             el.child("a.color-"+color).addClass("x-color-palette-sel");
24421             this.value = color;
24422             this.fireEvent("select", this, color);
24423         }
24424     }
24425 });/*
24426  * Based on:
24427  * Ext JS Library 1.1.1
24428  * Copyright(c) 2006-2007, Ext JS, LLC.
24429  *
24430  * Originally Released Under LGPL - original licence link has changed is not relivant.
24431  *
24432  * Fork - LGPL
24433  * <script type="text/javascript">
24434  */
24435  
24436 /**
24437  * @class Roo.DatePicker
24438  * @extends Roo.Component
24439  * Simple date picker class.
24440  * @constructor
24441  * Create a new DatePicker
24442  * @param {Object} config The config object
24443  */
24444 Roo.DatePicker = function(config){
24445     Roo.DatePicker.superclass.constructor.call(this, config);
24446
24447     this.value = config && config.value ?
24448                  config.value.clearTime() : new Date().clearTime();
24449
24450     this.addEvents({
24451         /**
24452              * @event select
24453              * Fires when a date is selected
24454              * @param {DatePicker} this
24455              * @param {Date} date The selected date
24456              */
24457         select: true
24458     });
24459
24460     if(this.handler){
24461         this.on("select", this.handler,  this.scope || this);
24462     }
24463     // build the disabledDatesRE
24464     if(!this.disabledDatesRE && this.disabledDates){
24465         var dd = this.disabledDates;
24466         var re = "(?:";
24467         for(var i = 0; i < dd.length; i++){
24468             re += dd[i];
24469             if(i != dd.length-1) re += "|";
24470         }
24471         this.disabledDatesRE = new RegExp(re + ")");
24472     }
24473 };
24474
24475 Roo.extend(Roo.DatePicker, Roo.Component, {
24476     /**
24477      * @cfg {String} todayText
24478      * The text to display on the button that selects the current date (defaults to "Today")
24479      */
24480     todayText : "Today",
24481     /**
24482      * @cfg {String} okText
24483      * The text to display on the ok button
24484      */
24485     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24486     /**
24487      * @cfg {String} cancelText
24488      * The text to display on the cancel button
24489      */
24490     cancelText : "Cancel",
24491     /**
24492      * @cfg {String} todayTip
24493      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24494      */
24495     todayTip : "{0} (Spacebar)",
24496     /**
24497      * @cfg {Date} minDate
24498      * Minimum allowable date (JavaScript date object, defaults to null)
24499      */
24500     minDate : null,
24501     /**
24502      * @cfg {Date} maxDate
24503      * Maximum allowable date (JavaScript date object, defaults to null)
24504      */
24505     maxDate : null,
24506     /**
24507      * @cfg {String} minText
24508      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24509      */
24510     minText : "This date is before the minimum date",
24511     /**
24512      * @cfg {String} maxText
24513      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24514      */
24515     maxText : "This date is after the maximum date",
24516     /**
24517      * @cfg {String} format
24518      * The default date format string which can be overriden for localization support.  The format must be
24519      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24520      */
24521     format : "m/d/y",
24522     /**
24523      * @cfg {Array} disabledDays
24524      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24525      */
24526     disabledDays : null,
24527     /**
24528      * @cfg {String} disabledDaysText
24529      * The tooltip to display when the date falls on a disabled day (defaults to "")
24530      */
24531     disabledDaysText : "",
24532     /**
24533      * @cfg {RegExp} disabledDatesRE
24534      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24535      */
24536     disabledDatesRE : null,
24537     /**
24538      * @cfg {String} disabledDatesText
24539      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24540      */
24541     disabledDatesText : "",
24542     /**
24543      * @cfg {Boolean} constrainToViewport
24544      * True to constrain the date picker to the viewport (defaults to true)
24545      */
24546     constrainToViewport : true,
24547     /**
24548      * @cfg {Array} monthNames
24549      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24550      */
24551     monthNames : Date.monthNames,
24552     /**
24553      * @cfg {Array} dayNames
24554      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24555      */
24556     dayNames : Date.dayNames,
24557     /**
24558      * @cfg {String} nextText
24559      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24560      */
24561     nextText: 'Next Month (Control+Right)',
24562     /**
24563      * @cfg {String} prevText
24564      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24565      */
24566     prevText: 'Previous Month (Control+Left)',
24567     /**
24568      * @cfg {String} monthYearText
24569      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24570      */
24571     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24572     /**
24573      * @cfg {Number} startDay
24574      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24575      */
24576     startDay : 0,
24577     /**
24578      * @cfg {Bool} showClear
24579      * Show a clear button (usefull for date form elements that can be blank.)
24580      */
24581     
24582     showClear: false,
24583     
24584     /**
24585      * Sets the value of the date field
24586      * @param {Date} value The date to set
24587      */
24588     setValue : function(value){
24589         var old = this.value;
24590         this.value = value.clearTime(true);
24591         if(this.el){
24592             this.update(this.value);
24593         }
24594     },
24595
24596     /**
24597      * Gets the current selected value of the date field
24598      * @return {Date} The selected date
24599      */
24600     getValue : function(){
24601         return this.value;
24602     },
24603
24604     // private
24605     focus : function(){
24606         if(this.el){
24607             this.update(this.activeDate);
24608         }
24609     },
24610
24611     // private
24612     onRender : function(container, position){
24613         var m = [
24614              '<table cellspacing="0">',
24615                 '<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>',
24616                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24617         var dn = this.dayNames;
24618         for(var i = 0; i < 7; i++){
24619             var d = this.startDay+i;
24620             if(d > 6){
24621                 d = d-7;
24622             }
24623             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24624         }
24625         m[m.length] = "</tr></thead><tbody><tr>";
24626         for(var i = 0; i < 42; i++) {
24627             if(i % 7 == 0 && i != 0){
24628                 m[m.length] = "</tr><tr>";
24629             }
24630             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24631         }
24632         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24633             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24634
24635         var el = document.createElement("div");
24636         el.className = "x-date-picker";
24637         el.innerHTML = m.join("");
24638
24639         container.dom.insertBefore(el, position);
24640
24641         this.el = Roo.get(el);
24642         this.eventEl = Roo.get(el.firstChild);
24643
24644         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24645             handler: this.showPrevMonth,
24646             scope: this,
24647             preventDefault:true,
24648             stopDefault:true
24649         });
24650
24651         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24652             handler: this.showNextMonth,
24653             scope: this,
24654             preventDefault:true,
24655             stopDefault:true
24656         });
24657
24658         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24659
24660         this.monthPicker = this.el.down('div.x-date-mp');
24661         this.monthPicker.enableDisplayMode('block');
24662         
24663         var kn = new Roo.KeyNav(this.eventEl, {
24664             "left" : function(e){
24665                 e.ctrlKey ?
24666                     this.showPrevMonth() :
24667                     this.update(this.activeDate.add("d", -1));
24668             },
24669
24670             "right" : function(e){
24671                 e.ctrlKey ?
24672                     this.showNextMonth() :
24673                     this.update(this.activeDate.add("d", 1));
24674             },
24675
24676             "up" : function(e){
24677                 e.ctrlKey ?
24678                     this.showNextYear() :
24679                     this.update(this.activeDate.add("d", -7));
24680             },
24681
24682             "down" : function(e){
24683                 e.ctrlKey ?
24684                     this.showPrevYear() :
24685                     this.update(this.activeDate.add("d", 7));
24686             },
24687
24688             "pageUp" : function(e){
24689                 this.showNextMonth();
24690             },
24691
24692             "pageDown" : function(e){
24693                 this.showPrevMonth();
24694             },
24695
24696             "enter" : function(e){
24697                 e.stopPropagation();
24698                 return true;
24699             },
24700
24701             scope : this
24702         });
24703
24704         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24705
24706         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24707
24708         this.el.unselectable();
24709         
24710         this.cells = this.el.select("table.x-date-inner tbody td");
24711         this.textNodes = this.el.query("table.x-date-inner tbody span");
24712
24713         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24714             text: "&#160;",
24715             tooltip: this.monthYearText
24716         });
24717
24718         this.mbtn.on('click', this.showMonthPicker, this);
24719         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24720
24721
24722         var today = (new Date()).dateFormat(this.format);
24723         
24724         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24725         if (this.showClear) {
24726             baseTb.add( new Roo.Toolbar.Fill());
24727         }
24728         baseTb.add({
24729             text: String.format(this.todayText, today),
24730             tooltip: String.format(this.todayTip, today),
24731             handler: this.selectToday,
24732             scope: this
24733         });
24734         
24735         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24736             
24737         //});
24738         if (this.showClear) {
24739             
24740             baseTb.add( new Roo.Toolbar.Fill());
24741             baseTb.add({
24742                 text: '&#160;',
24743                 cls: 'x-btn-icon x-btn-clear',
24744                 handler: function() {
24745                     //this.value = '';
24746                     this.fireEvent("select", this, '');
24747                 },
24748                 scope: this
24749             });
24750         }
24751         
24752         
24753         if(Roo.isIE){
24754             this.el.repaint();
24755         }
24756         this.update(this.value);
24757     },
24758
24759     createMonthPicker : function(){
24760         if(!this.monthPicker.dom.firstChild){
24761             var buf = ['<table border="0" cellspacing="0">'];
24762             for(var i = 0; i < 6; i++){
24763                 buf.push(
24764                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24765                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24766                     i == 0 ?
24767                     '<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>' :
24768                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24769                 );
24770             }
24771             buf.push(
24772                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24773                     this.okText,
24774                     '</button><button type="button" class="x-date-mp-cancel">',
24775                     this.cancelText,
24776                     '</button></td></tr>',
24777                 '</table>'
24778             );
24779             this.monthPicker.update(buf.join(''));
24780             this.monthPicker.on('click', this.onMonthClick, this);
24781             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24782
24783             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24784             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24785
24786             this.mpMonths.each(function(m, a, i){
24787                 i += 1;
24788                 if((i%2) == 0){
24789                     m.dom.xmonth = 5 + Math.round(i * .5);
24790                 }else{
24791                     m.dom.xmonth = Math.round((i-1) * .5);
24792                 }
24793             });
24794         }
24795     },
24796
24797     showMonthPicker : function(){
24798         this.createMonthPicker();
24799         var size = this.el.getSize();
24800         this.monthPicker.setSize(size);
24801         this.monthPicker.child('table').setSize(size);
24802
24803         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24804         this.updateMPMonth(this.mpSelMonth);
24805         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24806         this.updateMPYear(this.mpSelYear);
24807
24808         this.monthPicker.slideIn('t', {duration:.2});
24809     },
24810
24811     updateMPYear : function(y){
24812         this.mpyear = y;
24813         var ys = this.mpYears.elements;
24814         for(var i = 1; i <= 10; i++){
24815             var td = ys[i-1], y2;
24816             if((i%2) == 0){
24817                 y2 = y + Math.round(i * .5);
24818                 td.firstChild.innerHTML = y2;
24819                 td.xyear = y2;
24820             }else{
24821                 y2 = y - (5-Math.round(i * .5));
24822                 td.firstChild.innerHTML = y2;
24823                 td.xyear = y2;
24824             }
24825             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24826         }
24827     },
24828
24829     updateMPMonth : function(sm){
24830         this.mpMonths.each(function(m, a, i){
24831             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24832         });
24833     },
24834
24835     selectMPMonth: function(m){
24836         
24837     },
24838
24839     onMonthClick : function(e, t){
24840         e.stopEvent();
24841         var el = new Roo.Element(t), pn;
24842         if(el.is('button.x-date-mp-cancel')){
24843             this.hideMonthPicker();
24844         }
24845         else if(el.is('button.x-date-mp-ok')){
24846             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24847             this.hideMonthPicker();
24848         }
24849         else if(pn = el.up('td.x-date-mp-month', 2)){
24850             this.mpMonths.removeClass('x-date-mp-sel');
24851             pn.addClass('x-date-mp-sel');
24852             this.mpSelMonth = pn.dom.xmonth;
24853         }
24854         else if(pn = el.up('td.x-date-mp-year', 2)){
24855             this.mpYears.removeClass('x-date-mp-sel');
24856             pn.addClass('x-date-mp-sel');
24857             this.mpSelYear = pn.dom.xyear;
24858         }
24859         else if(el.is('a.x-date-mp-prev')){
24860             this.updateMPYear(this.mpyear-10);
24861         }
24862         else if(el.is('a.x-date-mp-next')){
24863             this.updateMPYear(this.mpyear+10);
24864         }
24865     },
24866
24867     onMonthDblClick : function(e, t){
24868         e.stopEvent();
24869         var el = new Roo.Element(t), pn;
24870         if(pn = el.up('td.x-date-mp-month', 2)){
24871             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24872             this.hideMonthPicker();
24873         }
24874         else if(pn = el.up('td.x-date-mp-year', 2)){
24875             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24876             this.hideMonthPicker();
24877         }
24878     },
24879
24880     hideMonthPicker : function(disableAnim){
24881         if(this.monthPicker){
24882             if(disableAnim === true){
24883                 this.monthPicker.hide();
24884             }else{
24885                 this.monthPicker.slideOut('t', {duration:.2});
24886             }
24887         }
24888     },
24889
24890     // private
24891     showPrevMonth : function(e){
24892         this.update(this.activeDate.add("mo", -1));
24893     },
24894
24895     // private
24896     showNextMonth : function(e){
24897         this.update(this.activeDate.add("mo", 1));
24898     },
24899
24900     // private
24901     showPrevYear : function(){
24902         this.update(this.activeDate.add("y", -1));
24903     },
24904
24905     // private
24906     showNextYear : function(){
24907         this.update(this.activeDate.add("y", 1));
24908     },
24909
24910     // private
24911     handleMouseWheel : function(e){
24912         var delta = e.getWheelDelta();
24913         if(delta > 0){
24914             this.showPrevMonth();
24915             e.stopEvent();
24916         } else if(delta < 0){
24917             this.showNextMonth();
24918             e.stopEvent();
24919         }
24920     },
24921
24922     // private
24923     handleDateClick : function(e, t){
24924         e.stopEvent();
24925         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24926             this.setValue(new Date(t.dateValue));
24927             this.fireEvent("select", this, this.value);
24928         }
24929     },
24930
24931     // private
24932     selectToday : function(){
24933         this.setValue(new Date().clearTime());
24934         this.fireEvent("select", this, this.value);
24935     },
24936
24937     // private
24938     update : function(date){
24939         var vd = this.activeDate;
24940         this.activeDate = date;
24941         if(vd && this.el){
24942             var t = date.getTime();
24943             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24944                 this.cells.removeClass("x-date-selected");
24945                 this.cells.each(function(c){
24946                    if(c.dom.firstChild.dateValue == t){
24947                        c.addClass("x-date-selected");
24948                        setTimeout(function(){
24949                             try{c.dom.firstChild.focus();}catch(e){}
24950                        }, 50);
24951                        return false;
24952                    }
24953                 });
24954                 return;
24955             }
24956         }
24957         var days = date.getDaysInMonth();
24958         var firstOfMonth = date.getFirstDateOfMonth();
24959         var startingPos = firstOfMonth.getDay()-this.startDay;
24960
24961         if(startingPos <= this.startDay){
24962             startingPos += 7;
24963         }
24964
24965         var pm = date.add("mo", -1);
24966         var prevStart = pm.getDaysInMonth()-startingPos;
24967
24968         var cells = this.cells.elements;
24969         var textEls = this.textNodes;
24970         days += startingPos;
24971
24972         // convert everything to numbers so it's fast
24973         var day = 86400000;
24974         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24975         var today = new Date().clearTime().getTime();
24976         var sel = date.clearTime().getTime();
24977         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24978         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24979         var ddMatch = this.disabledDatesRE;
24980         var ddText = this.disabledDatesText;
24981         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24982         var ddaysText = this.disabledDaysText;
24983         var format = this.format;
24984
24985         var setCellClass = function(cal, cell){
24986             cell.title = "";
24987             var t = d.getTime();
24988             cell.firstChild.dateValue = t;
24989             if(t == today){
24990                 cell.className += " x-date-today";
24991                 cell.title = cal.todayText;
24992             }
24993             if(t == sel){
24994                 cell.className += " x-date-selected";
24995                 setTimeout(function(){
24996                     try{cell.firstChild.focus();}catch(e){}
24997                 }, 50);
24998             }
24999             // disabling
25000             if(t < min) {
25001                 cell.className = " x-date-disabled";
25002                 cell.title = cal.minText;
25003                 return;
25004             }
25005             if(t > max) {
25006                 cell.className = " x-date-disabled";
25007                 cell.title = cal.maxText;
25008                 return;
25009             }
25010             if(ddays){
25011                 if(ddays.indexOf(d.getDay()) != -1){
25012                     cell.title = ddaysText;
25013                     cell.className = " x-date-disabled";
25014                 }
25015             }
25016             if(ddMatch && format){
25017                 var fvalue = d.dateFormat(format);
25018                 if(ddMatch.test(fvalue)){
25019                     cell.title = ddText.replace("%0", fvalue);
25020                     cell.className = " x-date-disabled";
25021                 }
25022             }
25023         };
25024
25025         var i = 0;
25026         for(; i < startingPos; i++) {
25027             textEls[i].innerHTML = (++prevStart);
25028             d.setDate(d.getDate()+1);
25029             cells[i].className = "x-date-prevday";
25030             setCellClass(this, cells[i]);
25031         }
25032         for(; i < days; i++){
25033             intDay = i - startingPos + 1;
25034             textEls[i].innerHTML = (intDay);
25035             d.setDate(d.getDate()+1);
25036             cells[i].className = "x-date-active";
25037             setCellClass(this, cells[i]);
25038         }
25039         var extraDays = 0;
25040         for(; i < 42; i++) {
25041              textEls[i].innerHTML = (++extraDays);
25042              d.setDate(d.getDate()+1);
25043              cells[i].className = "x-date-nextday";
25044              setCellClass(this, cells[i]);
25045         }
25046
25047         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25048
25049         if(!this.internalRender){
25050             var main = this.el.dom.firstChild;
25051             var w = main.offsetWidth;
25052             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25053             Roo.fly(main).setWidth(w);
25054             this.internalRender = true;
25055             // opera does not respect the auto grow header center column
25056             // then, after it gets a width opera refuses to recalculate
25057             // without a second pass
25058             if(Roo.isOpera && !this.secondPass){
25059                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25060                 this.secondPass = true;
25061                 this.update.defer(10, this, [date]);
25062             }
25063         }
25064     }
25065 });/*
25066  * Based on:
25067  * Ext JS Library 1.1.1
25068  * Copyright(c) 2006-2007, Ext JS, LLC.
25069  *
25070  * Originally Released Under LGPL - original licence link has changed is not relivant.
25071  *
25072  * Fork - LGPL
25073  * <script type="text/javascript">
25074  */
25075 /**
25076  * @class Roo.TabPanel
25077  * @extends Roo.util.Observable
25078  * A lightweight tab container.
25079  * <br><br>
25080  * Usage:
25081  * <pre><code>
25082 // basic tabs 1, built from existing content
25083 var tabs = new Roo.TabPanel("tabs1");
25084 tabs.addTab("script", "View Script");
25085 tabs.addTab("markup", "View Markup");
25086 tabs.activate("script");
25087
25088 // more advanced tabs, built from javascript
25089 var jtabs = new Roo.TabPanel("jtabs");
25090 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25091
25092 // set up the UpdateManager
25093 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25094 var updater = tab2.getUpdateManager();
25095 updater.setDefaultUrl("ajax1.htm");
25096 tab2.on('activate', updater.refresh, updater, true);
25097
25098 // Use setUrl for Ajax loading
25099 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25100 tab3.setUrl("ajax2.htm", null, true);
25101
25102 // Disabled tab
25103 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25104 tab4.disable();
25105
25106 jtabs.activate("jtabs-1");
25107  * </code></pre>
25108  * @constructor
25109  * Create a new TabPanel.
25110  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25111  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25112  */
25113 Roo.TabPanel = function(container, config){
25114     /**
25115     * The container element for this TabPanel.
25116     * @type Roo.Element
25117     */
25118     this.el = Roo.get(container, true);
25119     if(config){
25120         if(typeof config == "boolean"){
25121             this.tabPosition = config ? "bottom" : "top";
25122         }else{
25123             Roo.apply(this, config);
25124         }
25125     }
25126     if(this.tabPosition == "bottom"){
25127         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25128         this.el.addClass("x-tabs-bottom");
25129     }
25130     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25131     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25132     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25133     if(Roo.isIE){
25134         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25135     }
25136     if(this.tabPosition != "bottom"){
25137     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25138      * @type Roo.Element
25139      */
25140       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25141       this.el.addClass("x-tabs-top");
25142     }
25143     this.items = [];
25144
25145     this.bodyEl.setStyle("position", "relative");
25146
25147     this.active = null;
25148     this.activateDelegate = this.activate.createDelegate(this);
25149
25150     this.addEvents({
25151         /**
25152          * @event tabchange
25153          * Fires when the active tab changes
25154          * @param {Roo.TabPanel} this
25155          * @param {Roo.TabPanelItem} activePanel The new active tab
25156          */
25157         "tabchange": true,
25158         /**
25159          * @event beforetabchange
25160          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25161          * @param {Roo.TabPanel} this
25162          * @param {Object} e Set cancel to true on this object to cancel the tab change
25163          * @param {Roo.TabPanelItem} tab The tab being changed to
25164          */
25165         "beforetabchange" : true
25166     });
25167
25168     Roo.EventManager.onWindowResize(this.onResize, this);
25169     this.cpad = this.el.getPadding("lr");
25170     this.hiddenCount = 0;
25171
25172     Roo.TabPanel.superclass.constructor.call(this);
25173 };
25174
25175 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25176         /*
25177          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25178          */
25179     tabPosition : "top",
25180         /*
25181          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25182          */
25183     currentTabWidth : 0,
25184         /*
25185          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25186          */
25187     minTabWidth : 40,
25188         /*
25189          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25190          */
25191     maxTabWidth : 250,
25192         /*
25193          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25194          */
25195     preferredTabWidth : 175,
25196         /*
25197          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25198          */
25199     resizeTabs : false,
25200         /*
25201          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25202          */
25203     monitorResize : true,
25204
25205     /**
25206      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25207      * @param {String} id The id of the div to use <b>or create</b>
25208      * @param {String} text The text for the tab
25209      * @param {String} content (optional) Content to put in the TabPanelItem body
25210      * @param {Boolean} closable (optional) True to create a close icon on the tab
25211      * @return {Roo.TabPanelItem} The created TabPanelItem
25212      */
25213     addTab : function(id, text, content, closable){
25214         var item = new Roo.TabPanelItem(this, id, text, closable);
25215         this.addTabItem(item);
25216         if(content){
25217             item.setContent(content);
25218         }
25219         return item;
25220     },
25221
25222     /**
25223      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25224      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25225      * @return {Roo.TabPanelItem}
25226      */
25227     getTab : function(id){
25228         return this.items[id];
25229     },
25230
25231     /**
25232      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25233      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25234      */
25235     hideTab : function(id){
25236         var t = this.items[id];
25237         if(!t.isHidden()){
25238            t.setHidden(true);
25239            this.hiddenCount++;
25240            this.autoSizeTabs();
25241         }
25242     },
25243
25244     /**
25245      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25246      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25247      */
25248     unhideTab : function(id){
25249         var t = this.items[id];
25250         if(t.isHidden()){
25251            t.setHidden(false);
25252            this.hiddenCount--;
25253            this.autoSizeTabs();
25254         }
25255     },
25256
25257     /**
25258      * Adds an existing {@link Roo.TabPanelItem}.
25259      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25260      */
25261     addTabItem : function(item){
25262         this.items[item.id] = item;
25263         this.items.push(item);
25264         if(this.resizeTabs){
25265            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25266            this.autoSizeTabs();
25267         }else{
25268             item.autoSize();
25269         }
25270     },
25271
25272     /**
25273      * Removes a {@link Roo.TabPanelItem}.
25274      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25275      */
25276     removeTab : function(id){
25277         var items = this.items;
25278         var tab = items[id];
25279         if(!tab) { return; }
25280         var index = items.indexOf(tab);
25281         if(this.active == tab && items.length > 1){
25282             var newTab = this.getNextAvailable(index);
25283             if(newTab) {
25284                 newTab.activate();
25285             }
25286         }
25287         this.stripEl.dom.removeChild(tab.pnode.dom);
25288         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25289             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25290         }
25291         items.splice(index, 1);
25292         delete this.items[tab.id];
25293         tab.fireEvent("close", tab);
25294         tab.purgeListeners();
25295         this.autoSizeTabs();
25296     },
25297
25298     getNextAvailable : function(start){
25299         var items = this.items;
25300         var index = start;
25301         // look for a next tab that will slide over to
25302         // replace the one being removed
25303         while(index < items.length){
25304             var item = items[++index];
25305             if(item && !item.isHidden()){
25306                 return item;
25307             }
25308         }
25309         // if one isn't found select the previous tab (on the left)
25310         index = start;
25311         while(index >= 0){
25312             var item = items[--index];
25313             if(item && !item.isHidden()){
25314                 return item;
25315             }
25316         }
25317         return null;
25318     },
25319
25320     /**
25321      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25322      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25323      */
25324     disableTab : function(id){
25325         var tab = this.items[id];
25326         if(tab && this.active != tab){
25327             tab.disable();
25328         }
25329     },
25330
25331     /**
25332      * Enables a {@link Roo.TabPanelItem} that is disabled.
25333      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25334      */
25335     enableTab : function(id){
25336         var tab = this.items[id];
25337         tab.enable();
25338     },
25339
25340     /**
25341      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25342      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25343      * @return {Roo.TabPanelItem} The TabPanelItem.
25344      */
25345     activate : function(id){
25346         var tab = this.items[id];
25347         if(!tab){
25348             return null;
25349         }
25350         if(tab == this.active || tab.disabled){
25351             return tab;
25352         }
25353         var e = {};
25354         this.fireEvent("beforetabchange", this, e, tab);
25355         if(e.cancel !== true && !tab.disabled){
25356             if(this.active){
25357                 this.active.hide();
25358             }
25359             this.active = this.items[id];
25360             this.active.show();
25361             this.fireEvent("tabchange", this, this.active);
25362         }
25363         return tab;
25364     },
25365
25366     /**
25367      * Gets the active {@link Roo.TabPanelItem}.
25368      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25369      */
25370     getActiveTab : function(){
25371         return this.active;
25372     },
25373
25374     /**
25375      * Updates the tab body element to fit the height of the container element
25376      * for overflow scrolling
25377      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25378      */
25379     syncHeight : function(targetHeight){
25380         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25381         var bm = this.bodyEl.getMargins();
25382         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25383         this.bodyEl.setHeight(newHeight);
25384         return newHeight;
25385     },
25386
25387     onResize : function(){
25388         if(this.monitorResize){
25389             this.autoSizeTabs();
25390         }
25391     },
25392
25393     /**
25394      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25395      */
25396     beginUpdate : function(){
25397         this.updating = true;
25398     },
25399
25400     /**
25401      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25402      */
25403     endUpdate : function(){
25404         this.updating = false;
25405         this.autoSizeTabs();
25406     },
25407
25408     /**
25409      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25410      */
25411     autoSizeTabs : function(){
25412         var count = this.items.length;
25413         var vcount = count - this.hiddenCount;
25414         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25415         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25416         var availWidth = Math.floor(w / vcount);
25417         var b = this.stripBody;
25418         if(b.getWidth() > w){
25419             var tabs = this.items;
25420             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25421             if(availWidth < this.minTabWidth){
25422                 /*if(!this.sleft){    // incomplete scrolling code
25423                     this.createScrollButtons();
25424                 }
25425                 this.showScroll();
25426                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25427             }
25428         }else{
25429             if(this.currentTabWidth < this.preferredTabWidth){
25430                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25431             }
25432         }
25433     },
25434
25435     /**
25436      * Returns the number of tabs in this TabPanel.
25437      * @return {Number}
25438      */
25439      getCount : function(){
25440          return this.items.length;
25441      },
25442
25443     /**
25444      * Resizes all the tabs to the passed width
25445      * @param {Number} The new width
25446      */
25447     setTabWidth : function(width){
25448         this.currentTabWidth = width;
25449         for(var i = 0, len = this.items.length; i < len; i++) {
25450                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25451         }
25452     },
25453
25454     /**
25455      * Destroys this TabPanel
25456      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25457      */
25458     destroy : function(removeEl){
25459         Roo.EventManager.removeResizeListener(this.onResize, this);
25460         for(var i = 0, len = this.items.length; i < len; i++){
25461             this.items[i].purgeListeners();
25462         }
25463         if(removeEl === true){
25464             this.el.update("");
25465             this.el.remove();
25466         }
25467     }
25468 });
25469
25470 /**
25471  * @class Roo.TabPanelItem
25472  * @extends Roo.util.Observable
25473  * Represents an individual item (tab plus body) in a TabPanel.
25474  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25475  * @param {String} id The id of this TabPanelItem
25476  * @param {String} text The text for the tab of this TabPanelItem
25477  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25478  */
25479 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25480     /**
25481      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25482      * @type Roo.TabPanel
25483      */
25484     this.tabPanel = tabPanel;
25485     /**
25486      * The id for this TabPanelItem
25487      * @type String
25488      */
25489     this.id = id;
25490     /** @private */
25491     this.disabled = false;
25492     /** @private */
25493     this.text = text;
25494     /** @private */
25495     this.loaded = false;
25496     this.closable = closable;
25497
25498     /**
25499      * The body element for this TabPanelItem.
25500      * @type Roo.Element
25501      */
25502     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25503     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25504     this.bodyEl.setStyle("display", "block");
25505     this.bodyEl.setStyle("zoom", "1");
25506     this.hideAction();
25507
25508     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25509     /** @private */
25510     this.el = Roo.get(els.el, true);
25511     this.inner = Roo.get(els.inner, true);
25512     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25513     this.pnode = Roo.get(els.el.parentNode, true);
25514     this.el.on("mousedown", this.onTabMouseDown, this);
25515     this.el.on("click", this.onTabClick, this);
25516     /** @private */
25517     if(closable){
25518         var c = Roo.get(els.close, true);
25519         c.dom.title = this.closeText;
25520         c.addClassOnOver("close-over");
25521         c.on("click", this.closeClick, this);
25522      }
25523
25524     this.addEvents({
25525          /**
25526          * @event activate
25527          * Fires when this tab becomes the active tab.
25528          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25529          * @param {Roo.TabPanelItem} this
25530          */
25531         "activate": true,
25532         /**
25533          * @event beforeclose
25534          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25535          * @param {Roo.TabPanelItem} this
25536          * @param {Object} e Set cancel to true on this object to cancel the close.
25537          */
25538         "beforeclose": true,
25539         /**
25540          * @event close
25541          * Fires when this tab is closed.
25542          * @param {Roo.TabPanelItem} this
25543          */
25544          "close": true,
25545         /**
25546          * @event deactivate
25547          * Fires when this tab is no longer the active tab.
25548          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25549          * @param {Roo.TabPanelItem} this
25550          */
25551          "deactivate" : true
25552     });
25553     this.hidden = false;
25554
25555     Roo.TabPanelItem.superclass.constructor.call(this);
25556 };
25557
25558 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25559     purgeListeners : function(){
25560        Roo.util.Observable.prototype.purgeListeners.call(this);
25561        this.el.removeAllListeners();
25562     },
25563     /**
25564      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25565      */
25566     show : function(){
25567         this.pnode.addClass("on");
25568         this.showAction();
25569         if(Roo.isOpera){
25570             this.tabPanel.stripWrap.repaint();
25571         }
25572         this.fireEvent("activate", this.tabPanel, this);
25573     },
25574
25575     /**
25576      * Returns true if this tab is the active tab.
25577      * @return {Boolean}
25578      */
25579     isActive : function(){
25580         return this.tabPanel.getActiveTab() == this;
25581     },
25582
25583     /**
25584      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25585      */
25586     hide : function(){
25587         this.pnode.removeClass("on");
25588         this.hideAction();
25589         this.fireEvent("deactivate", this.tabPanel, this);
25590     },
25591
25592     hideAction : function(){
25593         this.bodyEl.hide();
25594         this.bodyEl.setStyle("position", "absolute");
25595         this.bodyEl.setLeft("-20000px");
25596         this.bodyEl.setTop("-20000px");
25597     },
25598
25599     showAction : function(){
25600         this.bodyEl.setStyle("position", "relative");
25601         this.bodyEl.setTop("");
25602         this.bodyEl.setLeft("");
25603         this.bodyEl.show();
25604     },
25605
25606     /**
25607      * Set the tooltip for the tab.
25608      * @param {String} tooltip The tab's tooltip
25609      */
25610     setTooltip : function(text){
25611         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25612             this.textEl.dom.qtip = text;
25613             this.textEl.dom.removeAttribute('title');
25614         }else{
25615             this.textEl.dom.title = text;
25616         }
25617     },
25618
25619     onTabClick : function(e){
25620         e.preventDefault();
25621         this.tabPanel.activate(this.id);
25622     },
25623
25624     onTabMouseDown : function(e){
25625         e.preventDefault();
25626         this.tabPanel.activate(this.id);
25627     },
25628
25629     getWidth : function(){
25630         return this.inner.getWidth();
25631     },
25632
25633     setWidth : function(width){
25634         var iwidth = width - this.pnode.getPadding("lr");
25635         this.inner.setWidth(iwidth);
25636         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25637         this.pnode.setWidth(width);
25638     },
25639
25640     /**
25641      * Show or hide the tab
25642      * @param {Boolean} hidden True to hide or false to show.
25643      */
25644     setHidden : function(hidden){
25645         this.hidden = hidden;
25646         this.pnode.setStyle("display", hidden ? "none" : "");
25647     },
25648
25649     /**
25650      * Returns true if this tab is "hidden"
25651      * @return {Boolean}
25652      */
25653     isHidden : function(){
25654         return this.hidden;
25655     },
25656
25657     /**
25658      * Returns the text for this tab
25659      * @return {String}
25660      */
25661     getText : function(){
25662         return this.text;
25663     },
25664
25665     autoSize : function(){
25666         //this.el.beginMeasure();
25667         this.textEl.setWidth(1);
25668         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25669         //this.el.endMeasure();
25670     },
25671
25672     /**
25673      * Sets the text for the tab (Note: this also sets the tooltip text)
25674      * @param {String} text The tab's text and tooltip
25675      */
25676     setText : function(text){
25677         this.text = text;
25678         this.textEl.update(text);
25679         this.setTooltip(text);
25680         if(!this.tabPanel.resizeTabs){
25681             this.autoSize();
25682         }
25683     },
25684     /**
25685      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25686      */
25687     activate : function(){
25688         this.tabPanel.activate(this.id);
25689     },
25690
25691     /**
25692      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25693      */
25694     disable : function(){
25695         if(this.tabPanel.active != this){
25696             this.disabled = true;
25697             this.pnode.addClass("disabled");
25698         }
25699     },
25700
25701     /**
25702      * Enables this TabPanelItem if it was previously disabled.
25703      */
25704     enable : function(){
25705         this.disabled = false;
25706         this.pnode.removeClass("disabled");
25707     },
25708
25709     /**
25710      * Sets the content for this TabPanelItem.
25711      * @param {String} content The content
25712      * @param {Boolean} loadScripts true to look for and load scripts
25713      */
25714     setContent : function(content, loadScripts){
25715         this.bodyEl.update(content, loadScripts);
25716     },
25717
25718     /**
25719      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25720      * @return {Roo.UpdateManager} The UpdateManager
25721      */
25722     getUpdateManager : function(){
25723         return this.bodyEl.getUpdateManager();
25724     },
25725
25726     /**
25727      * Set a URL to be used to load the content for this TabPanelItem.
25728      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25729      * @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)
25730      * @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)
25731      * @return {Roo.UpdateManager} The UpdateManager
25732      */
25733     setUrl : function(url, params, loadOnce){
25734         if(this.refreshDelegate){
25735             this.un('activate', this.refreshDelegate);
25736         }
25737         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25738         this.on("activate", this.refreshDelegate);
25739         return this.bodyEl.getUpdateManager();
25740     },
25741
25742     /** @private */
25743     _handleRefresh : function(url, params, loadOnce){
25744         if(!loadOnce || !this.loaded){
25745             var updater = this.bodyEl.getUpdateManager();
25746             updater.update(url, params, this._setLoaded.createDelegate(this));
25747         }
25748     },
25749
25750     /**
25751      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25752      *   Will fail silently if the setUrl method has not been called.
25753      *   This does not activate the panel, just updates its content.
25754      */
25755     refresh : function(){
25756         if(this.refreshDelegate){
25757            this.loaded = false;
25758            this.refreshDelegate();
25759         }
25760     },
25761
25762     /** @private */
25763     _setLoaded : function(){
25764         this.loaded = true;
25765     },
25766
25767     /** @private */
25768     closeClick : function(e){
25769         var o = {};
25770         e.stopEvent();
25771         this.fireEvent("beforeclose", this, o);
25772         if(o.cancel !== true){
25773             this.tabPanel.removeTab(this.id);
25774         }
25775     },
25776     /**
25777      * The text displayed in the tooltip for the close icon.
25778      * @type String
25779      */
25780     closeText : "Close this tab"
25781 });
25782
25783 /** @private */
25784 Roo.TabPanel.prototype.createStrip = function(container){
25785     var strip = document.createElement("div");
25786     strip.className = "x-tabs-wrap";
25787     container.appendChild(strip);
25788     return strip;
25789 };
25790 /** @private */
25791 Roo.TabPanel.prototype.createStripList = function(strip){
25792     // div wrapper for retard IE
25793     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>';
25794     return strip.firstChild.firstChild.firstChild.firstChild;
25795 };
25796 /** @private */
25797 Roo.TabPanel.prototype.createBody = function(container){
25798     var body = document.createElement("div");
25799     Roo.id(body, "tab-body");
25800     Roo.fly(body).addClass("x-tabs-body");
25801     container.appendChild(body);
25802     return body;
25803 };
25804 /** @private */
25805 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25806     var body = Roo.getDom(id);
25807     if(!body){
25808         body = document.createElement("div");
25809         body.id = id;
25810     }
25811     Roo.fly(body).addClass("x-tabs-item-body");
25812     bodyEl.insertBefore(body, bodyEl.firstChild);
25813     return body;
25814 };
25815 /** @private */
25816 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25817     var td = document.createElement("td");
25818     stripEl.appendChild(td);
25819     if(closable){
25820         td.className = "x-tabs-closable";
25821         if(!this.closeTpl){
25822             this.closeTpl = new Roo.Template(
25823                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25824                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25825                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25826             );
25827         }
25828         var el = this.closeTpl.overwrite(td, {"text": text});
25829         var close = el.getElementsByTagName("div")[0];
25830         var inner = el.getElementsByTagName("em")[0];
25831         return {"el": el, "close": close, "inner": inner};
25832     } else {
25833         if(!this.tabTpl){
25834             this.tabTpl = new Roo.Template(
25835                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25836                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25837             );
25838         }
25839         var el = this.tabTpl.overwrite(td, {"text": text});
25840         var inner = el.getElementsByTagName("em")[0];
25841         return {"el": el, "inner": inner};
25842     }
25843 };/*
25844  * Based on:
25845  * Ext JS Library 1.1.1
25846  * Copyright(c) 2006-2007, Ext JS, LLC.
25847  *
25848  * Originally Released Under LGPL - original licence link has changed is not relivant.
25849  *
25850  * Fork - LGPL
25851  * <script type="text/javascript">
25852  */
25853
25854 /**
25855  * @class Roo.Button
25856  * @extends Roo.util.Observable
25857  * Simple Button class
25858  * @cfg {String} text The button text
25859  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25860  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25861  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25862  * @cfg {Object} scope The scope of the handler
25863  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25864  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25865  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25866  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25867  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25868  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25869    applies if enableToggle = true)
25870  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25871  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25872   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25873  * @constructor
25874  * Create a new button
25875  * @param {Object} config The config object
25876  */
25877 Roo.Button = function(renderTo, config)
25878 {
25879     if (!config) {
25880         config = renderTo;
25881         renderTo = config.renderTo || false;
25882     }
25883     
25884     Roo.apply(this, config);
25885     this.addEvents({
25886         /**
25887              * @event click
25888              * Fires when this button is clicked
25889              * @param {Button} this
25890              * @param {EventObject} e The click event
25891              */
25892             "click" : true,
25893         /**
25894              * @event toggle
25895              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25896              * @param {Button} this
25897              * @param {Boolean} pressed
25898              */
25899             "toggle" : true,
25900         /**
25901              * @event mouseover
25902              * Fires when the mouse hovers over the button
25903              * @param {Button} this
25904              * @param {Event} e The event object
25905              */
25906         'mouseover' : true,
25907         /**
25908              * @event mouseout
25909              * Fires when the mouse exits the button
25910              * @param {Button} this
25911              * @param {Event} e The event object
25912              */
25913         'mouseout': true,
25914          /**
25915              * @event render
25916              * Fires when the button is rendered
25917              * @param {Button} this
25918              */
25919         'render': true
25920     });
25921     if(this.menu){
25922         this.menu = Roo.menu.MenuMgr.get(this.menu);
25923     }
25924     // register listeners first!!  - so render can be captured..
25925     Roo.util.Observable.call(this);
25926     if(renderTo){
25927         this.render(renderTo);
25928     }
25929     
25930   
25931 };
25932
25933 Roo.extend(Roo.Button, Roo.util.Observable, {
25934     /**
25935      * 
25936      */
25937     
25938     /**
25939      * Read-only. True if this button is hidden
25940      * @type Boolean
25941      */
25942     hidden : false,
25943     /**
25944      * Read-only. True if this button is disabled
25945      * @type Boolean
25946      */
25947     disabled : false,
25948     /**
25949      * Read-only. True if this button is pressed (only if enableToggle = true)
25950      * @type Boolean
25951      */
25952     pressed : false,
25953
25954     /**
25955      * @cfg {Number} tabIndex 
25956      * The DOM tabIndex for this button (defaults to undefined)
25957      */
25958     tabIndex : undefined,
25959
25960     /**
25961      * @cfg {Boolean} enableToggle
25962      * True to enable pressed/not pressed toggling (defaults to false)
25963      */
25964     enableToggle: false,
25965     /**
25966      * @cfg {Mixed} menu
25967      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25968      */
25969     menu : undefined,
25970     /**
25971      * @cfg {String} menuAlign
25972      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25973      */
25974     menuAlign : "tl-bl?",
25975
25976     /**
25977      * @cfg {String} iconCls
25978      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25979      */
25980     iconCls : undefined,
25981     /**
25982      * @cfg {String} type
25983      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25984      */
25985     type : 'button',
25986
25987     // private
25988     menuClassTarget: 'tr',
25989
25990     /**
25991      * @cfg {String} clickEvent
25992      * The type of event to map to the button's event handler (defaults to 'click')
25993      */
25994     clickEvent : 'click',
25995
25996     /**
25997      * @cfg {Boolean} handleMouseEvents
25998      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25999      */
26000     handleMouseEvents : true,
26001
26002     /**
26003      * @cfg {String} tooltipType
26004      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26005      */
26006     tooltipType : 'qtip',
26007
26008     /**
26009      * @cfg {String} cls
26010      * A CSS class to apply to the button's main element.
26011      */
26012     
26013     /**
26014      * @cfg {Roo.Template} template (Optional)
26015      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26016      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26017      * require code modifications if required elements (e.g. a button) aren't present.
26018      */
26019
26020     // private
26021     render : function(renderTo){
26022         var btn;
26023         if(this.hideParent){
26024             this.parentEl = Roo.get(renderTo);
26025         }
26026         if(!this.dhconfig){
26027             if(!this.template){
26028                 if(!Roo.Button.buttonTemplate){
26029                     // hideous table template
26030                     Roo.Button.buttonTemplate = new Roo.Template(
26031                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26032                         '<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>',
26033                         "</tr></tbody></table>");
26034                 }
26035                 this.template = Roo.Button.buttonTemplate;
26036             }
26037             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26038             var btnEl = btn.child("button:first");
26039             btnEl.on('focus', this.onFocus, this);
26040             btnEl.on('blur', this.onBlur, this);
26041             if(this.cls){
26042                 btn.addClass(this.cls);
26043             }
26044             if(this.icon){
26045                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26046             }
26047             if(this.iconCls){
26048                 btnEl.addClass(this.iconCls);
26049                 if(!this.cls){
26050                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26051                 }
26052             }
26053             if(this.tabIndex !== undefined){
26054                 btnEl.dom.tabIndex = this.tabIndex;
26055             }
26056             if(this.tooltip){
26057                 if(typeof this.tooltip == 'object'){
26058                     Roo.QuickTips.tips(Roo.apply({
26059                           target: btnEl.id
26060                     }, this.tooltip));
26061                 } else {
26062                     btnEl.dom[this.tooltipType] = this.tooltip;
26063                 }
26064             }
26065         }else{
26066             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26067         }
26068         this.el = btn;
26069         if(this.id){
26070             this.el.dom.id = this.el.id = this.id;
26071         }
26072         if(this.menu){
26073             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26074             this.menu.on("show", this.onMenuShow, this);
26075             this.menu.on("hide", this.onMenuHide, this);
26076         }
26077         btn.addClass("x-btn");
26078         if(Roo.isIE && !Roo.isIE7){
26079             this.autoWidth.defer(1, this);
26080         }else{
26081             this.autoWidth();
26082         }
26083         if(this.handleMouseEvents){
26084             btn.on("mouseover", this.onMouseOver, this);
26085             btn.on("mouseout", this.onMouseOut, this);
26086             btn.on("mousedown", this.onMouseDown, this);
26087         }
26088         btn.on(this.clickEvent, this.onClick, this);
26089         //btn.on("mouseup", this.onMouseUp, this);
26090         if(this.hidden){
26091             this.hide();
26092         }
26093         if(this.disabled){
26094             this.disable();
26095         }
26096         Roo.ButtonToggleMgr.register(this);
26097         if(this.pressed){
26098             this.el.addClass("x-btn-pressed");
26099         }
26100         if(this.repeat){
26101             var repeater = new Roo.util.ClickRepeater(btn,
26102                 typeof this.repeat == "object" ? this.repeat : {}
26103             );
26104             repeater.on("click", this.onClick,  this);
26105         }
26106         
26107         this.fireEvent('render', this);
26108         
26109     },
26110     /**
26111      * Returns the button's underlying element
26112      * @return {Roo.Element} The element
26113      */
26114     getEl : function(){
26115         return this.el;  
26116     },
26117     
26118     /**
26119      * Destroys this Button and removes any listeners.
26120      */
26121     destroy : function(){
26122         Roo.ButtonToggleMgr.unregister(this);
26123         this.el.removeAllListeners();
26124         this.purgeListeners();
26125         this.el.remove();
26126     },
26127
26128     // private
26129     autoWidth : function(){
26130         if(this.el){
26131             this.el.setWidth("auto");
26132             if(Roo.isIE7 && Roo.isStrict){
26133                 var ib = this.el.child('button');
26134                 if(ib && ib.getWidth() > 20){
26135                     ib.clip();
26136                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26137                 }
26138             }
26139             if(this.minWidth){
26140                 if(this.hidden){
26141                     this.el.beginMeasure();
26142                 }
26143                 if(this.el.getWidth() < this.minWidth){
26144                     this.el.setWidth(this.minWidth);
26145                 }
26146                 if(this.hidden){
26147                     this.el.endMeasure();
26148                 }
26149             }
26150         }
26151     },
26152
26153     /**
26154      * Assigns this button's click handler
26155      * @param {Function} handler The function to call when the button is clicked
26156      * @param {Object} scope (optional) Scope for the function passed in
26157      */
26158     setHandler : function(handler, scope){
26159         this.handler = handler;
26160         this.scope = scope;  
26161     },
26162     
26163     /**
26164      * Sets this button's text
26165      * @param {String} text The button text
26166      */
26167     setText : function(text){
26168         this.text = text;
26169         if(this.el){
26170             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26171         }
26172         this.autoWidth();
26173     },
26174     
26175     /**
26176      * Gets the text for this button
26177      * @return {String} The button text
26178      */
26179     getText : function(){
26180         return this.text;  
26181     },
26182     
26183     /**
26184      * Show this button
26185      */
26186     show: function(){
26187         this.hidden = false;
26188         if(this.el){
26189             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26190         }
26191     },
26192     
26193     /**
26194      * Hide this button
26195      */
26196     hide: function(){
26197         this.hidden = true;
26198         if(this.el){
26199             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26200         }
26201     },
26202     
26203     /**
26204      * Convenience function for boolean show/hide
26205      * @param {Boolean} visible True to show, false to hide
26206      */
26207     setVisible: function(visible){
26208         if(visible) {
26209             this.show();
26210         }else{
26211             this.hide();
26212         }
26213     },
26214     
26215     /**
26216      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26217      * @param {Boolean} state (optional) Force a particular state
26218      */
26219     toggle : function(state){
26220         state = state === undefined ? !this.pressed : state;
26221         if(state != this.pressed){
26222             if(state){
26223                 this.el.addClass("x-btn-pressed");
26224                 this.pressed = true;
26225                 this.fireEvent("toggle", this, true);
26226             }else{
26227                 this.el.removeClass("x-btn-pressed");
26228                 this.pressed = false;
26229                 this.fireEvent("toggle", this, false);
26230             }
26231             if(this.toggleHandler){
26232                 this.toggleHandler.call(this.scope || this, this, state);
26233             }
26234         }
26235     },
26236     
26237     /**
26238      * Focus the button
26239      */
26240     focus : function(){
26241         this.el.child('button:first').focus();
26242     },
26243     
26244     /**
26245      * Disable this button
26246      */
26247     disable : function(){
26248         if(this.el){
26249             this.el.addClass("x-btn-disabled");
26250         }
26251         this.disabled = true;
26252     },
26253     
26254     /**
26255      * Enable this button
26256      */
26257     enable : function(){
26258         if(this.el){
26259             this.el.removeClass("x-btn-disabled");
26260         }
26261         this.disabled = false;
26262     },
26263
26264     /**
26265      * Convenience function for boolean enable/disable
26266      * @param {Boolean} enabled True to enable, false to disable
26267      */
26268     setDisabled : function(v){
26269         this[v !== true ? "enable" : "disable"]();
26270     },
26271
26272     // private
26273     onClick : function(e){
26274         if(e){
26275             e.preventDefault();
26276         }
26277         if(e.button != 0){
26278             return;
26279         }
26280         if(!this.disabled){
26281             if(this.enableToggle){
26282                 this.toggle();
26283             }
26284             if(this.menu && !this.menu.isVisible()){
26285                 this.menu.show(this.el, this.menuAlign);
26286             }
26287             this.fireEvent("click", this, e);
26288             if(this.handler){
26289                 this.el.removeClass("x-btn-over");
26290                 this.handler.call(this.scope || this, this, e);
26291             }
26292         }
26293     },
26294     // private
26295     onMouseOver : function(e){
26296         if(!this.disabled){
26297             this.el.addClass("x-btn-over");
26298             this.fireEvent('mouseover', this, e);
26299         }
26300     },
26301     // private
26302     onMouseOut : function(e){
26303         if(!e.within(this.el,  true)){
26304             this.el.removeClass("x-btn-over");
26305             this.fireEvent('mouseout', this, e);
26306         }
26307     },
26308     // private
26309     onFocus : function(e){
26310         if(!this.disabled){
26311             this.el.addClass("x-btn-focus");
26312         }
26313     },
26314     // private
26315     onBlur : function(e){
26316         this.el.removeClass("x-btn-focus");
26317     },
26318     // private
26319     onMouseDown : function(e){
26320         if(!this.disabled && e.button == 0){
26321             this.el.addClass("x-btn-click");
26322             Roo.get(document).on('mouseup', this.onMouseUp, this);
26323         }
26324     },
26325     // private
26326     onMouseUp : function(e){
26327         if(e.button == 0){
26328             this.el.removeClass("x-btn-click");
26329             Roo.get(document).un('mouseup', this.onMouseUp, this);
26330         }
26331     },
26332     // private
26333     onMenuShow : function(e){
26334         this.el.addClass("x-btn-menu-active");
26335     },
26336     // private
26337     onMenuHide : function(e){
26338         this.el.removeClass("x-btn-menu-active");
26339     }   
26340 });
26341
26342 // Private utility class used by Button
26343 Roo.ButtonToggleMgr = function(){
26344    var groups = {};
26345    
26346    function toggleGroup(btn, state){
26347        if(state){
26348            var g = groups[btn.toggleGroup];
26349            for(var i = 0, l = g.length; i < l; i++){
26350                if(g[i] != btn){
26351                    g[i].toggle(false);
26352                }
26353            }
26354        }
26355    }
26356    
26357    return {
26358        register : function(btn){
26359            if(!btn.toggleGroup){
26360                return;
26361            }
26362            var g = groups[btn.toggleGroup];
26363            if(!g){
26364                g = groups[btn.toggleGroup] = [];
26365            }
26366            g.push(btn);
26367            btn.on("toggle", toggleGroup);
26368        },
26369        
26370        unregister : function(btn){
26371            if(!btn.toggleGroup){
26372                return;
26373            }
26374            var g = groups[btn.toggleGroup];
26375            if(g){
26376                g.remove(btn);
26377                btn.un("toggle", toggleGroup);
26378            }
26379        }
26380    };
26381 }();/*
26382  * Based on:
26383  * Ext JS Library 1.1.1
26384  * Copyright(c) 2006-2007, Ext JS, LLC.
26385  *
26386  * Originally Released Under LGPL - original licence link has changed is not relivant.
26387  *
26388  * Fork - LGPL
26389  * <script type="text/javascript">
26390  */
26391  
26392 /**
26393  * @class Roo.SplitButton
26394  * @extends Roo.Button
26395  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26396  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26397  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26398  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26399  * @cfg {String} arrowTooltip The title attribute of the arrow
26400  * @constructor
26401  * Create a new menu button
26402  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26403  * @param {Object} config The config object
26404  */
26405 Roo.SplitButton = function(renderTo, config){
26406     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26407     /**
26408      * @event arrowclick
26409      * Fires when this button's arrow is clicked
26410      * @param {SplitButton} this
26411      * @param {EventObject} e The click event
26412      */
26413     this.addEvents({"arrowclick":true});
26414 };
26415
26416 Roo.extend(Roo.SplitButton, Roo.Button, {
26417     render : function(renderTo){
26418         // this is one sweet looking template!
26419         var tpl = new Roo.Template(
26420             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26421             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26422             '<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>',
26423             "</tbody></table></td><td>",
26424             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26425             '<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>',
26426             "</tbody></table></td></tr></table>"
26427         );
26428         var btn = tpl.append(renderTo, [this.text, this.type], true);
26429         var btnEl = btn.child("button");
26430         if(this.cls){
26431             btn.addClass(this.cls);
26432         }
26433         if(this.icon){
26434             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26435         }
26436         if(this.iconCls){
26437             btnEl.addClass(this.iconCls);
26438             if(!this.cls){
26439                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26440             }
26441         }
26442         this.el = btn;
26443         if(this.handleMouseEvents){
26444             btn.on("mouseover", this.onMouseOver, this);
26445             btn.on("mouseout", this.onMouseOut, this);
26446             btn.on("mousedown", this.onMouseDown, this);
26447             btn.on("mouseup", this.onMouseUp, this);
26448         }
26449         btn.on(this.clickEvent, this.onClick, this);
26450         if(this.tooltip){
26451             if(typeof this.tooltip == 'object'){
26452                 Roo.QuickTips.tips(Roo.apply({
26453                       target: btnEl.id
26454                 }, this.tooltip));
26455             } else {
26456                 btnEl.dom[this.tooltipType] = this.tooltip;
26457             }
26458         }
26459         if(this.arrowTooltip){
26460             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26461         }
26462         if(this.hidden){
26463             this.hide();
26464         }
26465         if(this.disabled){
26466             this.disable();
26467         }
26468         if(this.pressed){
26469             this.el.addClass("x-btn-pressed");
26470         }
26471         if(Roo.isIE && !Roo.isIE7){
26472             this.autoWidth.defer(1, this);
26473         }else{
26474             this.autoWidth();
26475         }
26476         if(this.menu){
26477             this.menu.on("show", this.onMenuShow, this);
26478             this.menu.on("hide", this.onMenuHide, this);
26479         }
26480         this.fireEvent('render', this);
26481     },
26482
26483     // private
26484     autoWidth : function(){
26485         if(this.el){
26486             var tbl = this.el.child("table:first");
26487             var tbl2 = this.el.child("table:last");
26488             this.el.setWidth("auto");
26489             tbl.setWidth("auto");
26490             if(Roo.isIE7 && Roo.isStrict){
26491                 var ib = this.el.child('button:first');
26492                 if(ib && ib.getWidth() > 20){
26493                     ib.clip();
26494                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26495                 }
26496             }
26497             if(this.minWidth){
26498                 if(this.hidden){
26499                     this.el.beginMeasure();
26500                 }
26501                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26502                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26503                 }
26504                 if(this.hidden){
26505                     this.el.endMeasure();
26506                 }
26507             }
26508             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26509         } 
26510     },
26511     /**
26512      * Sets this button's click handler
26513      * @param {Function} handler The function to call when the button is clicked
26514      * @param {Object} scope (optional) Scope for the function passed above
26515      */
26516     setHandler : function(handler, scope){
26517         this.handler = handler;
26518         this.scope = scope;  
26519     },
26520     
26521     /**
26522      * Sets this button's arrow click handler
26523      * @param {Function} handler The function to call when the arrow is clicked
26524      * @param {Object} scope (optional) Scope for the function passed above
26525      */
26526     setArrowHandler : function(handler, scope){
26527         this.arrowHandler = handler;
26528         this.scope = scope;  
26529     },
26530     
26531     /**
26532      * Focus the button
26533      */
26534     focus : function(){
26535         if(this.el){
26536             this.el.child("button:first").focus();
26537         }
26538     },
26539
26540     // private
26541     onClick : function(e){
26542         e.preventDefault();
26543         if(!this.disabled){
26544             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26545                 if(this.menu && !this.menu.isVisible()){
26546                     this.menu.show(this.el, this.menuAlign);
26547                 }
26548                 this.fireEvent("arrowclick", this, e);
26549                 if(this.arrowHandler){
26550                     this.arrowHandler.call(this.scope || this, this, e);
26551                 }
26552             }else{
26553                 this.fireEvent("click", this, e);
26554                 if(this.handler){
26555                     this.handler.call(this.scope || this, this, e);
26556                 }
26557             }
26558         }
26559     },
26560     // private
26561     onMouseDown : function(e){
26562         if(!this.disabled){
26563             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26564         }
26565     },
26566     // private
26567     onMouseUp : function(e){
26568         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26569     }   
26570 });
26571
26572
26573 // backwards compat
26574 Roo.MenuButton = Roo.SplitButton;/*
26575  * Based on:
26576  * Ext JS Library 1.1.1
26577  * Copyright(c) 2006-2007, Ext JS, LLC.
26578  *
26579  * Originally Released Under LGPL - original licence link has changed is not relivant.
26580  *
26581  * Fork - LGPL
26582  * <script type="text/javascript">
26583  */
26584
26585 /**
26586  * @class Roo.Toolbar
26587  * Basic Toolbar class.
26588  * @constructor
26589  * Creates a new Toolbar
26590  * @param {Object} config The config object
26591  */ 
26592 Roo.Toolbar = function(container, buttons, config)
26593 {
26594     /// old consturctor format still supported..
26595     if(container instanceof Array){ // omit the container for later rendering
26596         buttons = container;
26597         config = buttons;
26598         container = null;
26599     }
26600     if (typeof(container) == 'object' && container.xtype) {
26601         config = container;
26602         container = config.container;
26603         buttons = config.buttons; // not really - use items!!
26604     }
26605     var xitems = [];
26606     if (config && config.items) {
26607         xitems = config.items;
26608         delete config.items;
26609     }
26610     Roo.apply(this, config);
26611     this.buttons = buttons;
26612     
26613     if(container){
26614         this.render(container);
26615     }
26616     Roo.each(xitems, function(b) {
26617         this.add(b);
26618     }, this);
26619     
26620 };
26621
26622 Roo.Toolbar.prototype = {
26623     /**
26624      * @cfg {Roo.data.Store} items
26625      * array of button configs or elements to add
26626      */
26627     
26628     /**
26629      * @cfg {String/HTMLElement/Element} container
26630      * The id or element that will contain the toolbar
26631      */
26632     // private
26633     render : function(ct){
26634         this.el = Roo.get(ct);
26635         if(this.cls){
26636             this.el.addClass(this.cls);
26637         }
26638         // using a table allows for vertical alignment
26639         // 100% width is needed by Safari...
26640         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26641         this.tr = this.el.child("tr", true);
26642         var autoId = 0;
26643         this.items = new Roo.util.MixedCollection(false, function(o){
26644             return o.id || ("item" + (++autoId));
26645         });
26646         if(this.buttons){
26647             this.add.apply(this, this.buttons);
26648             delete this.buttons;
26649         }
26650     },
26651
26652     /**
26653      * Adds element(s) to the toolbar -- this function takes a variable number of 
26654      * arguments of mixed type and adds them to the toolbar.
26655      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26656      * <ul>
26657      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26658      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26659      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26660      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26661      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26662      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26663      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26664      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26665      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26666      * </ul>
26667      * @param {Mixed} arg2
26668      * @param {Mixed} etc.
26669      */
26670     add : function(){
26671         var a = arguments, l = a.length;
26672         for(var i = 0; i < l; i++){
26673             this._add(a[i]);
26674         }
26675     },
26676     // private..
26677     _add : function(el) {
26678         
26679         if (el.xtype) {
26680             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26681         }
26682         
26683         if (el.applyTo){ // some kind of form field
26684             return this.addField(el);
26685         } 
26686         if (el.render){ // some kind of Toolbar.Item
26687             return this.addItem(el);
26688         }
26689         if (typeof el == "string"){ // string
26690             if(el == "separator" || el == "-"){
26691                 return this.addSeparator();
26692             }
26693             if (el == " "){
26694                 return this.addSpacer();
26695             }
26696             if(el == "->"){
26697                 return this.addFill();
26698             }
26699             return this.addText(el);
26700             
26701         }
26702         if(el.tagName){ // element
26703             return this.addElement(el);
26704         }
26705         if(typeof el == "object"){ // must be button config?
26706             return this.addButton(el);
26707         }
26708         // and now what?!?!
26709         return false;
26710         
26711     },
26712     
26713     /**
26714      * Add an Xtype element
26715      * @param {Object} xtype Xtype Object
26716      * @return {Object} created Object
26717      */
26718     addxtype : function(e){
26719         return this.add(e);  
26720     },
26721     
26722     /**
26723      * Returns the Element for this toolbar.
26724      * @return {Roo.Element}
26725      */
26726     getEl : function(){
26727         return this.el;  
26728     },
26729     
26730     /**
26731      * Adds a separator
26732      * @return {Roo.Toolbar.Item} The separator item
26733      */
26734     addSeparator : function(){
26735         return this.addItem(new Roo.Toolbar.Separator());
26736     },
26737
26738     /**
26739      * Adds a spacer element
26740      * @return {Roo.Toolbar.Spacer} The spacer item
26741      */
26742     addSpacer : function(){
26743         return this.addItem(new Roo.Toolbar.Spacer());
26744     },
26745
26746     /**
26747      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26748      * @return {Roo.Toolbar.Fill} The fill item
26749      */
26750     addFill : function(){
26751         return this.addItem(new Roo.Toolbar.Fill());
26752     },
26753
26754     /**
26755      * Adds any standard HTML element to the toolbar
26756      * @param {String/HTMLElement/Element} el The element or id of the element to add
26757      * @return {Roo.Toolbar.Item} The element's item
26758      */
26759     addElement : function(el){
26760         return this.addItem(new Roo.Toolbar.Item(el));
26761     },
26762     /**
26763      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26764      * @type Roo.util.MixedCollection  
26765      */
26766     items : false,
26767      
26768     /**
26769      * Adds any Toolbar.Item or subclass
26770      * @param {Roo.Toolbar.Item} item
26771      * @return {Roo.Toolbar.Item} The item
26772      */
26773     addItem : function(item){
26774         var td = this.nextBlock();
26775         item.render(td);
26776         this.items.add(item);
26777         return item;
26778     },
26779     
26780     /**
26781      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26782      * @param {Object/Array} config A button config or array of configs
26783      * @return {Roo.Toolbar.Button/Array}
26784      */
26785     addButton : function(config){
26786         if(config instanceof Array){
26787             var buttons = [];
26788             for(var i = 0, len = config.length; i < len; i++) {
26789                 buttons.push(this.addButton(config[i]));
26790             }
26791             return buttons;
26792         }
26793         var b = config;
26794         if(!(config instanceof Roo.Toolbar.Button)){
26795             b = config.split ?
26796                 new Roo.Toolbar.SplitButton(config) :
26797                 new Roo.Toolbar.Button(config);
26798         }
26799         var td = this.nextBlock();
26800         b.render(td);
26801         this.items.add(b);
26802         return b;
26803     },
26804     
26805     /**
26806      * Adds text to the toolbar
26807      * @param {String} text The text to add
26808      * @return {Roo.Toolbar.Item} The element's item
26809      */
26810     addText : function(text){
26811         return this.addItem(new Roo.Toolbar.TextItem(text));
26812     },
26813     
26814     /**
26815      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26816      * @param {Number} index The index where the item is to be inserted
26817      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26818      * @return {Roo.Toolbar.Button/Item}
26819      */
26820     insertButton : function(index, item){
26821         if(item instanceof Array){
26822             var buttons = [];
26823             for(var i = 0, len = item.length; i < len; i++) {
26824                buttons.push(this.insertButton(index + i, item[i]));
26825             }
26826             return buttons;
26827         }
26828         if (!(item instanceof Roo.Toolbar.Button)){
26829            item = new Roo.Toolbar.Button(item);
26830         }
26831         var td = document.createElement("td");
26832         this.tr.insertBefore(td, this.tr.childNodes[index]);
26833         item.render(td);
26834         this.items.insert(index, item);
26835         return item;
26836     },
26837     
26838     /**
26839      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26840      * @param {Object} config
26841      * @return {Roo.Toolbar.Item} The element's item
26842      */
26843     addDom : function(config, returnEl){
26844         var td = this.nextBlock();
26845         Roo.DomHelper.overwrite(td, config);
26846         var ti = new Roo.Toolbar.Item(td.firstChild);
26847         ti.render(td);
26848         this.items.add(ti);
26849         return ti;
26850     },
26851
26852     /**
26853      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26854      * @type Roo.util.MixedCollection  
26855      */
26856     fields : false,
26857     
26858     /**
26859      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26860      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26861      * @param {Roo.form.Field} field
26862      * @return {Roo.ToolbarItem}
26863      */
26864      
26865       
26866     addField : function(field) {
26867         if (!this.fields) {
26868             var autoId = 0;
26869             this.fields = new Roo.util.MixedCollection(false, function(o){
26870                 return o.id || ("item" + (++autoId));
26871             });
26872
26873         }
26874         
26875         var td = this.nextBlock();
26876         field.render(td);
26877         var ti = new Roo.Toolbar.Item(td.firstChild);
26878         ti.render(td);
26879         this.items.add(ti);
26880         this.fields.add(field);
26881         return ti;
26882     },
26883     /**
26884      * Hide the toolbar
26885      * @method hide
26886      */
26887      
26888       
26889     hide : function()
26890     {
26891         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26892         this.el.child('div').hide();
26893     },
26894     /**
26895      * Show the toolbar
26896      * @method show
26897      */
26898     show : function()
26899     {
26900         this.el.child('div').show();
26901     },
26902       
26903     // private
26904     nextBlock : function(){
26905         var td = document.createElement("td");
26906         this.tr.appendChild(td);
26907         return td;
26908     },
26909
26910     // private
26911     destroy : function(){
26912         if(this.items){ // rendered?
26913             Roo.destroy.apply(Roo, this.items.items);
26914         }
26915         if(this.fields){ // rendered?
26916             Roo.destroy.apply(Roo, this.fields.items);
26917         }
26918         Roo.Element.uncache(this.el, this.tr);
26919     }
26920 };
26921
26922 /**
26923  * @class Roo.Toolbar.Item
26924  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26925  * @constructor
26926  * Creates a new Item
26927  * @param {HTMLElement} el 
26928  */
26929 Roo.Toolbar.Item = function(el){
26930     this.el = Roo.getDom(el);
26931     this.id = Roo.id(this.el);
26932     this.hidden = false;
26933 };
26934
26935 Roo.Toolbar.Item.prototype = {
26936     
26937     /**
26938      * Get this item's HTML Element
26939      * @return {HTMLElement}
26940      */
26941     getEl : function(){
26942        return this.el;  
26943     },
26944
26945     // private
26946     render : function(td){
26947         this.td = td;
26948         td.appendChild(this.el);
26949     },
26950     
26951     /**
26952      * Removes and destroys this item.
26953      */
26954     destroy : function(){
26955         this.td.parentNode.removeChild(this.td);
26956     },
26957     
26958     /**
26959      * Shows this item.
26960      */
26961     show: function(){
26962         this.hidden = false;
26963         this.td.style.display = "";
26964     },
26965     
26966     /**
26967      * Hides this item.
26968      */
26969     hide: function(){
26970         this.hidden = true;
26971         this.td.style.display = "none";
26972     },
26973     
26974     /**
26975      * Convenience function for boolean show/hide.
26976      * @param {Boolean} visible true to show/false to hide
26977      */
26978     setVisible: function(visible){
26979         if(visible) {
26980             this.show();
26981         }else{
26982             this.hide();
26983         }
26984     },
26985     
26986     /**
26987      * Try to focus this item.
26988      */
26989     focus : function(){
26990         Roo.fly(this.el).focus();
26991     },
26992     
26993     /**
26994      * Disables this item.
26995      */
26996     disable : function(){
26997         Roo.fly(this.td).addClass("x-item-disabled");
26998         this.disabled = true;
26999         this.el.disabled = true;
27000     },
27001     
27002     /**
27003      * Enables this item.
27004      */
27005     enable : function(){
27006         Roo.fly(this.td).removeClass("x-item-disabled");
27007         this.disabled = false;
27008         this.el.disabled = false;
27009     }
27010 };
27011
27012
27013 /**
27014  * @class Roo.Toolbar.Separator
27015  * @extends Roo.Toolbar.Item
27016  * A simple toolbar separator class
27017  * @constructor
27018  * Creates a new Separator
27019  */
27020 Roo.Toolbar.Separator = function(){
27021     var s = document.createElement("span");
27022     s.className = "ytb-sep";
27023     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27024 };
27025 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27026     enable:Roo.emptyFn,
27027     disable:Roo.emptyFn,
27028     focus:Roo.emptyFn
27029 });
27030
27031 /**
27032  * @class Roo.Toolbar.Spacer
27033  * @extends Roo.Toolbar.Item
27034  * A simple element that adds extra horizontal space to a toolbar.
27035  * @constructor
27036  * Creates a new Spacer
27037  */
27038 Roo.Toolbar.Spacer = function(){
27039     var s = document.createElement("div");
27040     s.className = "ytb-spacer";
27041     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27042 };
27043 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27044     enable:Roo.emptyFn,
27045     disable:Roo.emptyFn,
27046     focus:Roo.emptyFn
27047 });
27048
27049 /**
27050  * @class Roo.Toolbar.Fill
27051  * @extends Roo.Toolbar.Spacer
27052  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27053  * @constructor
27054  * Creates a new Spacer
27055  */
27056 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27057     // private
27058     render : function(td){
27059         td.style.width = '100%';
27060         Roo.Toolbar.Fill.superclass.render.call(this, td);
27061     }
27062 });
27063
27064 /**
27065  * @class Roo.Toolbar.TextItem
27066  * @extends Roo.Toolbar.Item
27067  * A simple class that renders text directly into a toolbar.
27068  * @constructor
27069  * Creates a new TextItem
27070  * @param {String} text
27071  */
27072 Roo.Toolbar.TextItem = function(text){
27073     if (typeof(text) == 'object') {
27074         text = text.text;
27075     }
27076     var s = document.createElement("span");
27077     s.className = "ytb-text";
27078     s.innerHTML = text;
27079     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27080 };
27081 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27082     enable:Roo.emptyFn,
27083     disable:Roo.emptyFn,
27084     focus:Roo.emptyFn
27085 });
27086
27087 /**
27088  * @class Roo.Toolbar.Button
27089  * @extends Roo.Button
27090  * A button that renders into a toolbar.
27091  * @constructor
27092  * Creates a new Button
27093  * @param {Object} config A standard {@link Roo.Button} config object
27094  */
27095 Roo.Toolbar.Button = function(config){
27096     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27097 };
27098 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27099     render : function(td){
27100         this.td = td;
27101         Roo.Toolbar.Button.superclass.render.call(this, td);
27102     },
27103     
27104     /**
27105      * Removes and destroys this button
27106      */
27107     destroy : function(){
27108         Roo.Toolbar.Button.superclass.destroy.call(this);
27109         this.td.parentNode.removeChild(this.td);
27110     },
27111     
27112     /**
27113      * Shows this button
27114      */
27115     show: function(){
27116         this.hidden = false;
27117         this.td.style.display = "";
27118     },
27119     
27120     /**
27121      * Hides this button
27122      */
27123     hide: function(){
27124         this.hidden = true;
27125         this.td.style.display = "none";
27126     },
27127
27128     /**
27129      * Disables this item
27130      */
27131     disable : function(){
27132         Roo.fly(this.td).addClass("x-item-disabled");
27133         this.disabled = true;
27134     },
27135
27136     /**
27137      * Enables this item
27138      */
27139     enable : function(){
27140         Roo.fly(this.td).removeClass("x-item-disabled");
27141         this.disabled = false;
27142     }
27143 });
27144 // backwards compat
27145 Roo.ToolbarButton = Roo.Toolbar.Button;
27146
27147 /**
27148  * @class Roo.Toolbar.SplitButton
27149  * @extends Roo.SplitButton
27150  * A menu button that renders into a toolbar.
27151  * @constructor
27152  * Creates a new SplitButton
27153  * @param {Object} config A standard {@link Roo.SplitButton} config object
27154  */
27155 Roo.Toolbar.SplitButton = function(config){
27156     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27157 };
27158 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27159     render : function(td){
27160         this.td = td;
27161         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27162     },
27163     
27164     /**
27165      * Removes and destroys this button
27166      */
27167     destroy : function(){
27168         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27169         this.td.parentNode.removeChild(this.td);
27170     },
27171     
27172     /**
27173      * Shows this button
27174      */
27175     show: function(){
27176         this.hidden = false;
27177         this.td.style.display = "";
27178     },
27179     
27180     /**
27181      * Hides this button
27182      */
27183     hide: function(){
27184         this.hidden = true;
27185         this.td.style.display = "none";
27186     }
27187 });
27188
27189 // backwards compat
27190 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27191  * Based on:
27192  * Ext JS Library 1.1.1
27193  * Copyright(c) 2006-2007, Ext JS, LLC.
27194  *
27195  * Originally Released Under LGPL - original licence link has changed is not relivant.
27196  *
27197  * Fork - LGPL
27198  * <script type="text/javascript">
27199  */
27200  
27201 /**
27202  * @class Roo.PagingToolbar
27203  * @extends Roo.Toolbar
27204  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27205  * @constructor
27206  * Create a new PagingToolbar
27207  * @param {Object} config The config object
27208  */
27209 Roo.PagingToolbar = function(el, ds, config)
27210 {
27211     // old args format still supported... - xtype is prefered..
27212     if (typeof(el) == 'object' && el.xtype) {
27213         // created from xtype...
27214         config = el;
27215         ds = el.dataSource;
27216         el = config.container;
27217     }
27218     var items = [];
27219     if (config.items) {
27220         items = config.items;
27221         config.items = [];
27222     }
27223     
27224     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27225     this.ds = ds;
27226     this.cursor = 0;
27227     this.renderButtons(this.el);
27228     this.bind(ds);
27229     
27230     // supprot items array.
27231    
27232     Roo.each(items, function(e) {
27233         this.add(Roo.factory(e));
27234     },this);
27235     
27236 };
27237
27238 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27239     /**
27240      * @cfg {Roo.data.Store} dataSource
27241      * The underlying data store providing the paged data
27242      */
27243     /**
27244      * @cfg {String/HTMLElement/Element} container
27245      * container The id or element that will contain the toolbar
27246      */
27247     /**
27248      * @cfg {Boolean} displayInfo
27249      * True to display the displayMsg (defaults to false)
27250      */
27251     /**
27252      * @cfg {Number} pageSize
27253      * The number of records to display per page (defaults to 20)
27254      */
27255     pageSize: 20,
27256     /**
27257      * @cfg {String} displayMsg
27258      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27259      */
27260     displayMsg : 'Displaying {0} - {1} of {2}',
27261     /**
27262      * @cfg {String} emptyMsg
27263      * The message to display when no records are found (defaults to "No data to display")
27264      */
27265     emptyMsg : 'No data to display',
27266     /**
27267      * Customizable piece of the default paging text (defaults to "Page")
27268      * @type String
27269      */
27270     beforePageText : "Page",
27271     /**
27272      * Customizable piece of the default paging text (defaults to "of %0")
27273      * @type String
27274      */
27275     afterPageText : "of {0}",
27276     /**
27277      * Customizable piece of the default paging text (defaults to "First Page")
27278      * @type String
27279      */
27280     firstText : "First Page",
27281     /**
27282      * Customizable piece of the default paging text (defaults to "Previous Page")
27283      * @type String
27284      */
27285     prevText : "Previous Page",
27286     /**
27287      * Customizable piece of the default paging text (defaults to "Next Page")
27288      * @type String
27289      */
27290     nextText : "Next Page",
27291     /**
27292      * Customizable piece of the default paging text (defaults to "Last Page")
27293      * @type String
27294      */
27295     lastText : "Last Page",
27296     /**
27297      * Customizable piece of the default paging text (defaults to "Refresh")
27298      * @type String
27299      */
27300     refreshText : "Refresh",
27301
27302     // private
27303     renderButtons : function(el){
27304         Roo.PagingToolbar.superclass.render.call(this, el);
27305         this.first = this.addButton({
27306             tooltip: this.firstText,
27307             cls: "x-btn-icon x-grid-page-first",
27308             disabled: true,
27309             handler: this.onClick.createDelegate(this, ["first"])
27310         });
27311         this.prev = this.addButton({
27312             tooltip: this.prevText,
27313             cls: "x-btn-icon x-grid-page-prev",
27314             disabled: true,
27315             handler: this.onClick.createDelegate(this, ["prev"])
27316         });
27317         //this.addSeparator();
27318         this.add(this.beforePageText);
27319         this.field = Roo.get(this.addDom({
27320            tag: "input",
27321            type: "text",
27322            size: "3",
27323            value: "1",
27324            cls: "x-grid-page-number"
27325         }).el);
27326         this.field.on("keydown", this.onPagingKeydown, this);
27327         this.field.on("focus", function(){this.dom.select();});
27328         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27329         this.field.setHeight(18);
27330         //this.addSeparator();
27331         this.next = this.addButton({
27332             tooltip: this.nextText,
27333             cls: "x-btn-icon x-grid-page-next",
27334             disabled: true,
27335             handler: this.onClick.createDelegate(this, ["next"])
27336         });
27337         this.last = this.addButton({
27338             tooltip: this.lastText,
27339             cls: "x-btn-icon x-grid-page-last",
27340             disabled: true,
27341             handler: this.onClick.createDelegate(this, ["last"])
27342         });
27343         //this.addSeparator();
27344         this.loading = this.addButton({
27345             tooltip: this.refreshText,
27346             cls: "x-btn-icon x-grid-loading",
27347             handler: this.onClick.createDelegate(this, ["refresh"])
27348         });
27349
27350         if(this.displayInfo){
27351             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27352         }
27353     },
27354
27355     // private
27356     updateInfo : function(){
27357         if(this.displayEl){
27358             var count = this.ds.getCount();
27359             var msg = count == 0 ?
27360                 this.emptyMsg :
27361                 String.format(
27362                     this.displayMsg,
27363                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27364                 );
27365             this.displayEl.update(msg);
27366         }
27367     },
27368
27369     // private
27370     onLoad : function(ds, r, o){
27371        this.cursor = o.params ? o.params.start : 0;
27372        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27373
27374        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27375        this.field.dom.value = ap;
27376        this.first.setDisabled(ap == 1);
27377        this.prev.setDisabled(ap == 1);
27378        this.next.setDisabled(ap == ps);
27379        this.last.setDisabled(ap == ps);
27380        this.loading.enable();
27381        this.updateInfo();
27382     },
27383
27384     // private
27385     getPageData : function(){
27386         var total = this.ds.getTotalCount();
27387         return {
27388             total : total,
27389             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27390             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27391         };
27392     },
27393
27394     // private
27395     onLoadError : function(){
27396         this.loading.enable();
27397     },
27398
27399     // private
27400     onPagingKeydown : function(e){
27401         var k = e.getKey();
27402         var d = this.getPageData();
27403         if(k == e.RETURN){
27404             var v = this.field.dom.value, pageNum;
27405             if(!v || isNaN(pageNum = parseInt(v, 10))){
27406                 this.field.dom.value = d.activePage;
27407                 return;
27408             }
27409             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27410             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27411             e.stopEvent();
27412         }
27413         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))
27414         {
27415           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27416           this.field.dom.value = pageNum;
27417           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27418           e.stopEvent();
27419         }
27420         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27421         {
27422           var v = this.field.dom.value, pageNum; 
27423           var increment = (e.shiftKey) ? 10 : 1;
27424           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27425             increment *= -1;
27426           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27427             this.field.dom.value = d.activePage;
27428             return;
27429           }
27430           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27431           {
27432             this.field.dom.value = parseInt(v, 10) + increment;
27433             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27434             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27435           }
27436           e.stopEvent();
27437         }
27438     },
27439
27440     // private
27441     beforeLoad : function(){
27442         if(this.loading){
27443             this.loading.disable();
27444         }
27445     },
27446
27447     // private
27448     onClick : function(which){
27449         var ds = this.ds;
27450         switch(which){
27451             case "first":
27452                 ds.load({params:{start: 0, limit: this.pageSize}});
27453             break;
27454             case "prev":
27455                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27456             break;
27457             case "next":
27458                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27459             break;
27460             case "last":
27461                 var total = ds.getTotalCount();
27462                 var extra = total % this.pageSize;
27463                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27464                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27465             break;
27466             case "refresh":
27467                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27468             break;
27469         }
27470     },
27471
27472     /**
27473      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27474      * @param {Roo.data.Store} store The data store to unbind
27475      */
27476     unbind : function(ds){
27477         ds.un("beforeload", this.beforeLoad, this);
27478         ds.un("load", this.onLoad, this);
27479         ds.un("loadexception", this.onLoadError, this);
27480         ds.un("remove", this.updateInfo, this);
27481         ds.un("add", this.updateInfo, this);
27482         this.ds = undefined;
27483     },
27484
27485     /**
27486      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27487      * @param {Roo.data.Store} store The data store to bind
27488      */
27489     bind : function(ds){
27490         ds.on("beforeload", this.beforeLoad, this);
27491         ds.on("load", this.onLoad, this);
27492         ds.on("loadexception", this.onLoadError, this);
27493         ds.on("remove", this.updateInfo, this);
27494         ds.on("add", this.updateInfo, this);
27495         this.ds = ds;
27496     }
27497 });/*
27498  * Based on:
27499  * Ext JS Library 1.1.1
27500  * Copyright(c) 2006-2007, Ext JS, LLC.
27501  *
27502  * Originally Released Under LGPL - original licence link has changed is not relivant.
27503  *
27504  * Fork - LGPL
27505  * <script type="text/javascript">
27506  */
27507
27508 /**
27509  * @class Roo.Resizable
27510  * @extends Roo.util.Observable
27511  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27512  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27513  * 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
27514  * the element will be wrapped for you automatically.</p>
27515  * <p>Here is the list of valid resize handles:</p>
27516  * <pre>
27517 Value   Description
27518 ------  -------------------
27519  'n'     north
27520  's'     south
27521  'e'     east
27522  'w'     west
27523  'nw'    northwest
27524  'sw'    southwest
27525  'se'    southeast
27526  'ne'    northeast
27527  'hd'    horizontal drag
27528  'all'   all
27529 </pre>
27530  * <p>Here's an example showing the creation of a typical Resizable:</p>
27531  * <pre><code>
27532 var resizer = new Roo.Resizable("element-id", {
27533     handles: 'all',
27534     minWidth: 200,
27535     minHeight: 100,
27536     maxWidth: 500,
27537     maxHeight: 400,
27538     pinned: true
27539 });
27540 resizer.on("resize", myHandler);
27541 </code></pre>
27542  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27543  * resizer.east.setDisplayed(false);</p>
27544  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27545  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27546  * resize operation's new size (defaults to [0, 0])
27547  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27548  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27549  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27550  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27551  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27552  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27553  * @cfg {Number} width The width of the element in pixels (defaults to null)
27554  * @cfg {Number} height The height of the element in pixels (defaults to null)
27555  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27556  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27557  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27558  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27559  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27560  * in favor of the handles config option (defaults to false)
27561  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27562  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27563  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27564  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27565  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27566  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27567  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27568  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27569  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27570  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27571  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27572  * @constructor
27573  * Create a new resizable component
27574  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27575  * @param {Object} config configuration options
27576   */
27577 Roo.Resizable = function(el, config)
27578 {
27579     this.el = Roo.get(el);
27580
27581     if(config && config.wrap){
27582         config.resizeChild = this.el;
27583         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27584         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27585         this.el.setStyle("overflow", "hidden");
27586         this.el.setPositioning(config.resizeChild.getPositioning());
27587         config.resizeChild.clearPositioning();
27588         if(!config.width || !config.height){
27589             var csize = config.resizeChild.getSize();
27590             this.el.setSize(csize.width, csize.height);
27591         }
27592         if(config.pinned && !config.adjustments){
27593             config.adjustments = "auto";
27594         }
27595     }
27596
27597     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27598     this.proxy.unselectable();
27599     this.proxy.enableDisplayMode('block');
27600
27601     Roo.apply(this, config);
27602
27603     if(this.pinned){
27604         this.disableTrackOver = true;
27605         this.el.addClass("x-resizable-pinned");
27606     }
27607     // if the element isn't positioned, make it relative
27608     var position = this.el.getStyle("position");
27609     if(position != "absolute" && position != "fixed"){
27610         this.el.setStyle("position", "relative");
27611     }
27612     if(!this.handles){ // no handles passed, must be legacy style
27613         this.handles = 's,e,se';
27614         if(this.multiDirectional){
27615             this.handles += ',n,w';
27616         }
27617     }
27618     if(this.handles == "all"){
27619         this.handles = "n s e w ne nw se sw";
27620     }
27621     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27622     var ps = Roo.Resizable.positions;
27623     for(var i = 0, len = hs.length; i < len; i++){
27624         if(hs[i] && ps[hs[i]]){
27625             var pos = ps[hs[i]];
27626             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27627         }
27628     }
27629     // legacy
27630     this.corner = this.southeast;
27631     
27632     // updateBox = the box can move..
27633     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27634         this.updateBox = true;
27635     }
27636
27637     this.activeHandle = null;
27638
27639     if(this.resizeChild){
27640         if(typeof this.resizeChild == "boolean"){
27641             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27642         }else{
27643             this.resizeChild = Roo.get(this.resizeChild, true);
27644         }
27645     }
27646     
27647     if(this.adjustments == "auto"){
27648         var rc = this.resizeChild;
27649         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27650         if(rc && (hw || hn)){
27651             rc.position("relative");
27652             rc.setLeft(hw ? hw.el.getWidth() : 0);
27653             rc.setTop(hn ? hn.el.getHeight() : 0);
27654         }
27655         this.adjustments = [
27656             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27657             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27658         ];
27659     }
27660
27661     if(this.draggable){
27662         this.dd = this.dynamic ?
27663             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27664         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27665     }
27666
27667     // public events
27668     this.addEvents({
27669         /**
27670          * @event beforeresize
27671          * Fired before resize is allowed. Set enabled to false to cancel resize.
27672          * @param {Roo.Resizable} this
27673          * @param {Roo.EventObject} e The mousedown event
27674          */
27675         "beforeresize" : true,
27676         /**
27677          * @event resize
27678          * Fired after a resize.
27679          * @param {Roo.Resizable} this
27680          * @param {Number} width The new width
27681          * @param {Number} height The new height
27682          * @param {Roo.EventObject} e The mouseup event
27683          */
27684         "resize" : true
27685     });
27686
27687     if(this.width !== null && this.height !== null){
27688         this.resizeTo(this.width, this.height);
27689     }else{
27690         this.updateChildSize();
27691     }
27692     if(Roo.isIE){
27693         this.el.dom.style.zoom = 1;
27694     }
27695     Roo.Resizable.superclass.constructor.call(this);
27696 };
27697
27698 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27699         resizeChild : false,
27700         adjustments : [0, 0],
27701         minWidth : 5,
27702         minHeight : 5,
27703         maxWidth : 10000,
27704         maxHeight : 10000,
27705         enabled : true,
27706         animate : false,
27707         duration : .35,
27708         dynamic : false,
27709         handles : false,
27710         multiDirectional : false,
27711         disableTrackOver : false,
27712         easing : 'easeOutStrong',
27713         widthIncrement : 0,
27714         heightIncrement : 0,
27715         pinned : false,
27716         width : null,
27717         height : null,
27718         preserveRatio : false,
27719         transparent: false,
27720         minX: 0,
27721         minY: 0,
27722         draggable: false,
27723
27724         /**
27725          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27726          */
27727         constrainTo: undefined,
27728         /**
27729          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27730          */
27731         resizeRegion: undefined,
27732
27733
27734     /**
27735      * Perform a manual resize
27736      * @param {Number} width
27737      * @param {Number} height
27738      */
27739     resizeTo : function(width, height){
27740         this.el.setSize(width, height);
27741         this.updateChildSize();
27742         this.fireEvent("resize", this, width, height, null);
27743     },
27744
27745     // private
27746     startSizing : function(e, handle){
27747         this.fireEvent("beforeresize", this, e);
27748         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27749
27750             if(!this.overlay){
27751                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27752                 this.overlay.unselectable();
27753                 this.overlay.enableDisplayMode("block");
27754                 this.overlay.on("mousemove", this.onMouseMove, this);
27755                 this.overlay.on("mouseup", this.onMouseUp, this);
27756             }
27757             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27758
27759             this.resizing = true;
27760             this.startBox = this.el.getBox();
27761             this.startPoint = e.getXY();
27762             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27763                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27764
27765             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27766             this.overlay.show();
27767
27768             if(this.constrainTo) {
27769                 var ct = Roo.get(this.constrainTo);
27770                 this.resizeRegion = ct.getRegion().adjust(
27771                     ct.getFrameWidth('t'),
27772                     ct.getFrameWidth('l'),
27773                     -ct.getFrameWidth('b'),
27774                     -ct.getFrameWidth('r')
27775                 );
27776             }
27777
27778             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27779             this.proxy.show();
27780             this.proxy.setBox(this.startBox);
27781             if(!this.dynamic){
27782                 this.proxy.setStyle('visibility', 'visible');
27783             }
27784         }
27785     },
27786
27787     // private
27788     onMouseDown : function(handle, e){
27789         if(this.enabled){
27790             e.stopEvent();
27791             this.activeHandle = handle;
27792             this.startSizing(e, handle);
27793         }
27794     },
27795
27796     // private
27797     onMouseUp : function(e){
27798         var size = this.resizeElement();
27799         this.resizing = false;
27800         this.handleOut();
27801         this.overlay.hide();
27802         this.proxy.hide();
27803         this.fireEvent("resize", this, size.width, size.height, e);
27804     },
27805
27806     // private
27807     updateChildSize : function(){
27808         if(this.resizeChild){
27809             var el = this.el;
27810             var child = this.resizeChild;
27811             var adj = this.adjustments;
27812             if(el.dom.offsetWidth){
27813                 var b = el.getSize(true);
27814                 child.setSize(b.width+adj[0], b.height+adj[1]);
27815             }
27816             // Second call here for IE
27817             // The first call enables instant resizing and
27818             // the second call corrects scroll bars if they
27819             // exist
27820             if(Roo.isIE){
27821                 setTimeout(function(){
27822                     if(el.dom.offsetWidth){
27823                         var b = el.getSize(true);
27824                         child.setSize(b.width+adj[0], b.height+adj[1]);
27825                     }
27826                 }, 10);
27827             }
27828         }
27829     },
27830
27831     // private
27832     snap : function(value, inc, min){
27833         if(!inc || !value) return value;
27834         var newValue = value;
27835         var m = value % inc;
27836         if(m > 0){
27837             if(m > (inc/2)){
27838                 newValue = value + (inc-m);
27839             }else{
27840                 newValue = value - m;
27841             }
27842         }
27843         return Math.max(min, newValue);
27844     },
27845
27846     // private
27847     resizeElement : function(){
27848         var box = this.proxy.getBox();
27849         if(this.updateBox){
27850             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27851         }else{
27852             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27853         }
27854         this.updateChildSize();
27855         if(!this.dynamic){
27856             this.proxy.hide();
27857         }
27858         return box;
27859     },
27860
27861     // private
27862     constrain : function(v, diff, m, mx){
27863         if(v - diff < m){
27864             diff = v - m;
27865         }else if(v - diff > mx){
27866             diff = mx - v;
27867         }
27868         return diff;
27869     },
27870
27871     // private
27872     onMouseMove : function(e){
27873         if(this.enabled){
27874             try{// try catch so if something goes wrong the user doesn't get hung
27875
27876             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27877                 return;
27878             }
27879
27880             //var curXY = this.startPoint;
27881             var curSize = this.curSize || this.startBox;
27882             var x = this.startBox.x, y = this.startBox.y;
27883             var ox = x, oy = y;
27884             var w = curSize.width, h = curSize.height;
27885             var ow = w, oh = h;
27886             var mw = this.minWidth, mh = this.minHeight;
27887             var mxw = this.maxWidth, mxh = this.maxHeight;
27888             var wi = this.widthIncrement;
27889             var hi = this.heightIncrement;
27890
27891             var eventXY = e.getXY();
27892             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27893             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27894
27895             var pos = this.activeHandle.position;
27896
27897             switch(pos){
27898                 case "east":
27899                     w += diffX;
27900                     w = Math.min(Math.max(mw, w), mxw);
27901                     break;
27902              
27903                 case "south":
27904                     h += diffY;
27905                     h = Math.min(Math.max(mh, h), mxh);
27906                     break;
27907                 case "southeast":
27908                     w += diffX;
27909                     h += diffY;
27910                     w = Math.min(Math.max(mw, w), mxw);
27911                     h = Math.min(Math.max(mh, h), mxh);
27912                     break;
27913                 case "north":
27914                     diffY = this.constrain(h, diffY, mh, mxh);
27915                     y += diffY;
27916                     h -= diffY;
27917                     break;
27918                 case "hdrag":
27919                     
27920                     if (wi) {
27921                         var adiffX = Math.abs(diffX);
27922                         var sub = (adiffX % wi); // how much 
27923                         if (sub > (wi/2)) { // far enough to snap
27924                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27925                         } else {
27926                             // remove difference.. 
27927                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27928                         }
27929                     }
27930                     x += diffX;
27931                     x = Math.max(this.minX, x);
27932                     break;
27933                 case "west":
27934                     diffX = this.constrain(w, diffX, mw, mxw);
27935                     x += diffX;
27936                     w -= diffX;
27937                     break;
27938                 case "northeast":
27939                     w += diffX;
27940                     w = Math.min(Math.max(mw, w), mxw);
27941                     diffY = this.constrain(h, diffY, mh, mxh);
27942                     y += diffY;
27943                     h -= diffY;
27944                     break;
27945                 case "northwest":
27946                     diffX = this.constrain(w, diffX, mw, mxw);
27947                     diffY = this.constrain(h, diffY, mh, mxh);
27948                     y += diffY;
27949                     h -= diffY;
27950                     x += diffX;
27951                     w -= diffX;
27952                     break;
27953                case "southwest":
27954                     diffX = this.constrain(w, diffX, mw, mxw);
27955                     h += diffY;
27956                     h = Math.min(Math.max(mh, h), mxh);
27957                     x += diffX;
27958                     w -= diffX;
27959                     break;
27960             }
27961
27962             var sw = this.snap(w, wi, mw);
27963             var sh = this.snap(h, hi, mh);
27964             if(sw != w || sh != h){
27965                 switch(pos){
27966                     case "northeast":
27967                         y -= sh - h;
27968                     break;
27969                     case "north":
27970                         y -= sh - h;
27971                         break;
27972                     case "southwest":
27973                         x -= sw - w;
27974                     break;
27975                     case "west":
27976                         x -= sw - w;
27977                         break;
27978                     case "northwest":
27979                         x -= sw - w;
27980                         y -= sh - h;
27981                     break;
27982                 }
27983                 w = sw;
27984                 h = sh;
27985             }
27986
27987             if(this.preserveRatio){
27988                 switch(pos){
27989                     case "southeast":
27990                     case "east":
27991                         h = oh * (w/ow);
27992                         h = Math.min(Math.max(mh, h), mxh);
27993                         w = ow * (h/oh);
27994                        break;
27995                     case "south":
27996                         w = ow * (h/oh);
27997                         w = Math.min(Math.max(mw, w), mxw);
27998                         h = oh * (w/ow);
27999                         break;
28000                     case "northeast":
28001                         w = ow * (h/oh);
28002                         w = Math.min(Math.max(mw, w), mxw);
28003                         h = oh * (w/ow);
28004                     break;
28005                     case "north":
28006                         var tw = w;
28007                         w = ow * (h/oh);
28008                         w = Math.min(Math.max(mw, w), mxw);
28009                         h = oh * (w/ow);
28010                         x += (tw - w) / 2;
28011                         break;
28012                     case "southwest":
28013                         h = oh * (w/ow);
28014                         h = Math.min(Math.max(mh, h), mxh);
28015                         var tw = w;
28016                         w = ow * (h/oh);
28017                         x += tw - w;
28018                         break;
28019                     case "west":
28020                         var th = h;
28021                         h = oh * (w/ow);
28022                         h = Math.min(Math.max(mh, h), mxh);
28023                         y += (th - h) / 2;
28024                         var tw = w;
28025                         w = ow * (h/oh);
28026                         x += tw - w;
28027                        break;
28028                     case "northwest":
28029                         var tw = w;
28030                         var th = h;
28031                         h = oh * (w/ow);
28032                         h = Math.min(Math.max(mh, h), mxh);
28033                         w = ow * (h/oh);
28034                         y += th - h;
28035                         x += tw - w;
28036                        break;
28037
28038                 }
28039             }
28040             if (pos == 'hdrag') {
28041                 w = ow;
28042             }
28043             this.proxy.setBounds(x, y, w, h);
28044             if(this.dynamic){
28045                 this.resizeElement();
28046             }
28047             }catch(e){}
28048         }
28049     },
28050
28051     // private
28052     handleOver : function(){
28053         if(this.enabled){
28054             this.el.addClass("x-resizable-over");
28055         }
28056     },
28057
28058     // private
28059     handleOut : function(){
28060         if(!this.resizing){
28061             this.el.removeClass("x-resizable-over");
28062         }
28063     },
28064
28065     /**
28066      * Returns the element this component is bound to.
28067      * @return {Roo.Element}
28068      */
28069     getEl : function(){
28070         return this.el;
28071     },
28072
28073     /**
28074      * Returns the resizeChild element (or null).
28075      * @return {Roo.Element}
28076      */
28077     getResizeChild : function(){
28078         return this.resizeChild;
28079     },
28080
28081     /**
28082      * Destroys this resizable. If the element was wrapped and
28083      * removeEl is not true then the element remains.
28084      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28085      */
28086     destroy : function(removeEl){
28087         this.proxy.remove();
28088         if(this.overlay){
28089             this.overlay.removeAllListeners();
28090             this.overlay.remove();
28091         }
28092         var ps = Roo.Resizable.positions;
28093         for(var k in ps){
28094             if(typeof ps[k] != "function" && this[ps[k]]){
28095                 var h = this[ps[k]];
28096                 h.el.removeAllListeners();
28097                 h.el.remove();
28098             }
28099         }
28100         if(removeEl){
28101             this.el.update("");
28102             this.el.remove();
28103         }
28104     }
28105 });
28106
28107 // private
28108 // hash to map config positions to true positions
28109 Roo.Resizable.positions = {
28110     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28111     hd: "hdrag"
28112 };
28113
28114 // private
28115 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28116     if(!this.tpl){
28117         // only initialize the template if resizable is used
28118         var tpl = Roo.DomHelper.createTemplate(
28119             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28120         );
28121         tpl.compile();
28122         Roo.Resizable.Handle.prototype.tpl = tpl;
28123     }
28124     this.position = pos;
28125     this.rz = rz;
28126     // show north drag fro topdra
28127     var handlepos = pos == 'hdrag' ? 'north' : pos;
28128     
28129     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28130     if (pos == 'hdrag') {
28131         this.el.setStyle('cursor', 'pointer');
28132     }
28133     this.el.unselectable();
28134     if(transparent){
28135         this.el.setOpacity(0);
28136     }
28137     this.el.on("mousedown", this.onMouseDown, this);
28138     if(!disableTrackOver){
28139         this.el.on("mouseover", this.onMouseOver, this);
28140         this.el.on("mouseout", this.onMouseOut, this);
28141     }
28142 };
28143
28144 // private
28145 Roo.Resizable.Handle.prototype = {
28146     afterResize : function(rz){
28147         // do nothing
28148     },
28149     // private
28150     onMouseDown : function(e){
28151         this.rz.onMouseDown(this, e);
28152     },
28153     // private
28154     onMouseOver : function(e){
28155         this.rz.handleOver(this, e);
28156     },
28157     // private
28158     onMouseOut : function(e){
28159         this.rz.handleOut(this, e);
28160     }
28161 };/*
28162  * Based on:
28163  * Ext JS Library 1.1.1
28164  * Copyright(c) 2006-2007, Ext JS, LLC.
28165  *
28166  * Originally Released Under LGPL - original licence link has changed is not relivant.
28167  *
28168  * Fork - LGPL
28169  * <script type="text/javascript">
28170  */
28171
28172 /**
28173  * @class Roo.Editor
28174  * @extends Roo.Component
28175  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28176  * @constructor
28177  * Create a new Editor
28178  * @param {Roo.form.Field} field The Field object (or descendant)
28179  * @param {Object} config The config object
28180  */
28181 Roo.Editor = function(field, config){
28182     Roo.Editor.superclass.constructor.call(this, config);
28183     this.field = field;
28184     this.addEvents({
28185         /**
28186              * @event beforestartedit
28187              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28188              * false from the handler of this event.
28189              * @param {Editor} this
28190              * @param {Roo.Element} boundEl The underlying element bound to this editor
28191              * @param {Mixed} value The field value being set
28192              */
28193         "beforestartedit" : true,
28194         /**
28195              * @event startedit
28196              * Fires when this editor is displayed
28197              * @param {Roo.Element} boundEl The underlying element bound to this editor
28198              * @param {Mixed} value The starting field value
28199              */
28200         "startedit" : true,
28201         /**
28202              * @event beforecomplete
28203              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28204              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28205              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28206              * event will not fire since no edit actually occurred.
28207              * @param {Editor} this
28208              * @param {Mixed} value The current field value
28209              * @param {Mixed} startValue The original field value
28210              */
28211         "beforecomplete" : true,
28212         /**
28213              * @event complete
28214              * Fires after editing is complete and any changed value has been written to the underlying field.
28215              * @param {Editor} this
28216              * @param {Mixed} value The current field value
28217              * @param {Mixed} startValue The original field value
28218              */
28219         "complete" : true,
28220         /**
28221          * @event specialkey
28222          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28223          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28224          * @param {Roo.form.Field} this
28225          * @param {Roo.EventObject} e The event object
28226          */
28227         "specialkey" : true
28228     });
28229 };
28230
28231 Roo.extend(Roo.Editor, Roo.Component, {
28232     /**
28233      * @cfg {Boolean/String} autosize
28234      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28235      * or "height" to adopt the height only (defaults to false)
28236      */
28237     /**
28238      * @cfg {Boolean} revertInvalid
28239      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28240      * validation fails (defaults to true)
28241      */
28242     /**
28243      * @cfg {Boolean} ignoreNoChange
28244      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28245      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28246      * will never be ignored.
28247      */
28248     /**
28249      * @cfg {Boolean} hideEl
28250      * False to keep the bound element visible while the editor is displayed (defaults to true)
28251      */
28252     /**
28253      * @cfg {Mixed} value
28254      * The data value of the underlying field (defaults to "")
28255      */
28256     value : "",
28257     /**
28258      * @cfg {String} alignment
28259      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28260      */
28261     alignment: "c-c?",
28262     /**
28263      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28264      * for bottom-right shadow (defaults to "frame")
28265      */
28266     shadow : "frame",
28267     /**
28268      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28269      */
28270     constrain : false,
28271     /**
28272      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28273      */
28274     completeOnEnter : false,
28275     /**
28276      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28277      */
28278     cancelOnEsc : false,
28279     /**
28280      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28281      */
28282     updateEl : false,
28283
28284     // private
28285     onRender : function(ct, position){
28286         this.el = new Roo.Layer({
28287             shadow: this.shadow,
28288             cls: "x-editor",
28289             parentEl : ct,
28290             shim : this.shim,
28291             shadowOffset:4,
28292             id: this.id,
28293             constrain: this.constrain
28294         });
28295         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28296         if(this.field.msgTarget != 'title'){
28297             this.field.msgTarget = 'qtip';
28298         }
28299         this.field.render(this.el);
28300         if(Roo.isGecko){
28301             this.field.el.dom.setAttribute('autocomplete', 'off');
28302         }
28303         this.field.on("specialkey", this.onSpecialKey, this);
28304         if(this.swallowKeys){
28305             this.field.el.swallowEvent(['keydown','keypress']);
28306         }
28307         this.field.show();
28308         this.field.on("blur", this.onBlur, this);
28309         if(this.field.grow){
28310             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28311         }
28312     },
28313
28314     onSpecialKey : function(field, e)
28315     {
28316         //Roo.log('editor onSpecialKey');
28317         if(this.completeOnEnter && e.getKey() == e.ENTER){
28318             e.stopEvent();
28319             this.completeEdit();
28320             return;
28321         }
28322         // do not fire special key otherwise it might hide close the editor...
28323         if(e.getKey() == e.ENTER){    
28324             return;
28325         }
28326         if(this.cancelOnEsc && e.getKey() == e.ESC){
28327             this.cancelEdit();
28328             return;
28329         } 
28330         this.fireEvent('specialkey', field, e);
28331     
28332     },
28333
28334     /**
28335      * Starts the editing process and shows the editor.
28336      * @param {String/HTMLElement/Element} el The element to edit
28337      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28338       * to the innerHTML of el.
28339      */
28340     startEdit : function(el, value){
28341         if(this.editing){
28342             this.completeEdit();
28343         }
28344         this.boundEl = Roo.get(el);
28345         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28346         if(!this.rendered){
28347             this.render(this.parentEl || document.body);
28348         }
28349         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28350             return;
28351         }
28352         this.startValue = v;
28353         this.field.setValue(v);
28354         if(this.autoSize){
28355             var sz = this.boundEl.getSize();
28356             switch(this.autoSize){
28357                 case "width":
28358                 this.setSize(sz.width,  "");
28359                 break;
28360                 case "height":
28361                 this.setSize("",  sz.height);
28362                 break;
28363                 default:
28364                 this.setSize(sz.width,  sz.height);
28365             }
28366         }
28367         this.el.alignTo(this.boundEl, this.alignment);
28368         this.editing = true;
28369         if(Roo.QuickTips){
28370             Roo.QuickTips.disable();
28371         }
28372         this.show();
28373     },
28374
28375     /**
28376      * Sets the height and width of this editor.
28377      * @param {Number} width The new width
28378      * @param {Number} height The new height
28379      */
28380     setSize : function(w, h){
28381         this.field.setSize(w, h);
28382         if(this.el){
28383             this.el.sync();
28384         }
28385     },
28386
28387     /**
28388      * Realigns the editor to the bound field based on the current alignment config value.
28389      */
28390     realign : function(){
28391         this.el.alignTo(this.boundEl, this.alignment);
28392     },
28393
28394     /**
28395      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28396      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28397      */
28398     completeEdit : function(remainVisible){
28399         if(!this.editing){
28400             return;
28401         }
28402         var v = this.getValue();
28403         if(this.revertInvalid !== false && !this.field.isValid()){
28404             v = this.startValue;
28405             this.cancelEdit(true);
28406         }
28407         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28408             this.editing = false;
28409             this.hide();
28410             return;
28411         }
28412         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28413             this.editing = false;
28414             if(this.updateEl && this.boundEl){
28415                 this.boundEl.update(v);
28416             }
28417             if(remainVisible !== true){
28418                 this.hide();
28419             }
28420             this.fireEvent("complete", this, v, this.startValue);
28421         }
28422     },
28423
28424     // private
28425     onShow : function(){
28426         this.el.show();
28427         if(this.hideEl !== false){
28428             this.boundEl.hide();
28429         }
28430         this.field.show();
28431         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28432             this.fixIEFocus = true;
28433             this.deferredFocus.defer(50, this);
28434         }else{
28435             this.field.focus();
28436         }
28437         this.fireEvent("startedit", this.boundEl, this.startValue);
28438     },
28439
28440     deferredFocus : function(){
28441         if(this.editing){
28442             this.field.focus();
28443         }
28444     },
28445
28446     /**
28447      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28448      * reverted to the original starting value.
28449      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28450      * cancel (defaults to false)
28451      */
28452     cancelEdit : function(remainVisible){
28453         if(this.editing){
28454             this.setValue(this.startValue);
28455             if(remainVisible !== true){
28456                 this.hide();
28457             }
28458         }
28459     },
28460
28461     // private
28462     onBlur : function(){
28463         if(this.allowBlur !== true && this.editing){
28464             this.completeEdit();
28465         }
28466     },
28467
28468     // private
28469     onHide : function(){
28470         if(this.editing){
28471             this.completeEdit();
28472             return;
28473         }
28474         this.field.blur();
28475         if(this.field.collapse){
28476             this.field.collapse();
28477         }
28478         this.el.hide();
28479         if(this.hideEl !== false){
28480             this.boundEl.show();
28481         }
28482         if(Roo.QuickTips){
28483             Roo.QuickTips.enable();
28484         }
28485     },
28486
28487     /**
28488      * Sets the data value of the editor
28489      * @param {Mixed} value Any valid value supported by the underlying field
28490      */
28491     setValue : function(v){
28492         this.field.setValue(v);
28493     },
28494
28495     /**
28496      * Gets the data value of the editor
28497      * @return {Mixed} The data value
28498      */
28499     getValue : function(){
28500         return this.field.getValue();
28501     }
28502 });/*
28503  * Based on:
28504  * Ext JS Library 1.1.1
28505  * Copyright(c) 2006-2007, Ext JS, LLC.
28506  *
28507  * Originally Released Under LGPL - original licence link has changed is not relivant.
28508  *
28509  * Fork - LGPL
28510  * <script type="text/javascript">
28511  */
28512  
28513 /**
28514  * @class Roo.BasicDialog
28515  * @extends Roo.util.Observable
28516  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28517  * <pre><code>
28518 var dlg = new Roo.BasicDialog("my-dlg", {
28519     height: 200,
28520     width: 300,
28521     minHeight: 100,
28522     minWidth: 150,
28523     modal: true,
28524     proxyDrag: true,
28525     shadow: true
28526 });
28527 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28528 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28529 dlg.addButton('Cancel', dlg.hide, dlg);
28530 dlg.show();
28531 </code></pre>
28532   <b>A Dialog should always be a direct child of the body element.</b>
28533  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28534  * @cfg {String} title Default text to display in the title bar (defaults to null)
28535  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28536  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28537  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28538  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28539  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28540  * (defaults to null with no animation)
28541  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28542  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28543  * property for valid values (defaults to 'all')
28544  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28545  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28546  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28547  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28548  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28549  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28550  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28551  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28552  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28553  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28554  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28555  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28556  * draggable = true (defaults to false)
28557  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28558  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28559  * shadow (defaults to false)
28560  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28561  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28562  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28563  * @cfg {Array} buttons Array of buttons
28564  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28565  * @constructor
28566  * Create a new BasicDialog.
28567  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28568  * @param {Object} config Configuration options
28569  */
28570 Roo.BasicDialog = function(el, config){
28571     this.el = Roo.get(el);
28572     var dh = Roo.DomHelper;
28573     if(!this.el && config && config.autoCreate){
28574         if(typeof config.autoCreate == "object"){
28575             if(!config.autoCreate.id){
28576                 config.autoCreate.id = el;
28577             }
28578             this.el = dh.append(document.body,
28579                         config.autoCreate, true);
28580         }else{
28581             this.el = dh.append(document.body,
28582                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28583         }
28584     }
28585     el = this.el;
28586     el.setDisplayed(true);
28587     el.hide = this.hideAction;
28588     this.id = el.id;
28589     el.addClass("x-dlg");
28590
28591     Roo.apply(this, config);
28592
28593     this.proxy = el.createProxy("x-dlg-proxy");
28594     this.proxy.hide = this.hideAction;
28595     this.proxy.setOpacity(.5);
28596     this.proxy.hide();
28597
28598     if(config.width){
28599         el.setWidth(config.width);
28600     }
28601     if(config.height){
28602         el.setHeight(config.height);
28603     }
28604     this.size = el.getSize();
28605     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28606         this.xy = [config.x,config.y];
28607     }else{
28608         this.xy = el.getCenterXY(true);
28609     }
28610     /** The header element @type Roo.Element */
28611     this.header = el.child("> .x-dlg-hd");
28612     /** The body element @type Roo.Element */
28613     this.body = el.child("> .x-dlg-bd");
28614     /** The footer element @type Roo.Element */
28615     this.footer = el.child("> .x-dlg-ft");
28616
28617     if(!this.header){
28618         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28619     }
28620     if(!this.body){
28621         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28622     }
28623
28624     this.header.unselectable();
28625     if(this.title){
28626         this.header.update(this.title);
28627     }
28628     // this element allows the dialog to be focused for keyboard event
28629     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28630     this.focusEl.swallowEvent("click", true);
28631
28632     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28633
28634     // wrap the body and footer for special rendering
28635     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28636     if(this.footer){
28637         this.bwrap.dom.appendChild(this.footer.dom);
28638     }
28639
28640     this.bg = this.el.createChild({
28641         tag: "div", cls:"x-dlg-bg",
28642         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28643     });
28644     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28645
28646
28647     if(this.autoScroll !== false && !this.autoTabs){
28648         this.body.setStyle("overflow", "auto");
28649     }
28650
28651     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28652
28653     if(this.closable !== false){
28654         this.el.addClass("x-dlg-closable");
28655         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28656         this.close.on("click", this.closeClick, this);
28657         this.close.addClassOnOver("x-dlg-close-over");
28658     }
28659     if(this.collapsible !== false){
28660         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28661         this.collapseBtn.on("click", this.collapseClick, this);
28662         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28663         this.header.on("dblclick", this.collapseClick, this);
28664     }
28665     if(this.resizable !== false){
28666         this.el.addClass("x-dlg-resizable");
28667         this.resizer = new Roo.Resizable(el, {
28668             minWidth: this.minWidth || 80,
28669             minHeight:this.minHeight || 80,
28670             handles: this.resizeHandles || "all",
28671             pinned: true
28672         });
28673         this.resizer.on("beforeresize", this.beforeResize, this);
28674         this.resizer.on("resize", this.onResize, this);
28675     }
28676     if(this.draggable !== false){
28677         el.addClass("x-dlg-draggable");
28678         if (!this.proxyDrag) {
28679             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28680         }
28681         else {
28682             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28683         }
28684         dd.setHandleElId(this.header.id);
28685         dd.endDrag = this.endMove.createDelegate(this);
28686         dd.startDrag = this.startMove.createDelegate(this);
28687         dd.onDrag = this.onDrag.createDelegate(this);
28688         dd.scroll = false;
28689         this.dd = dd;
28690     }
28691     if(this.modal){
28692         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28693         this.mask.enableDisplayMode("block");
28694         this.mask.hide();
28695         this.el.addClass("x-dlg-modal");
28696     }
28697     if(this.shadow){
28698         this.shadow = new Roo.Shadow({
28699             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28700             offset : this.shadowOffset
28701         });
28702     }else{
28703         this.shadowOffset = 0;
28704     }
28705     if(Roo.useShims && this.shim !== false){
28706         this.shim = this.el.createShim();
28707         this.shim.hide = this.hideAction;
28708         this.shim.hide();
28709     }else{
28710         this.shim = false;
28711     }
28712     if(this.autoTabs){
28713         this.initTabs();
28714     }
28715     if (this.buttons) { 
28716         var bts= this.buttons;
28717         this.buttons = [];
28718         Roo.each(bts, function(b) {
28719             this.addButton(b);
28720         }, this);
28721     }
28722     
28723     
28724     this.addEvents({
28725         /**
28726          * @event keydown
28727          * Fires when a key is pressed
28728          * @param {Roo.BasicDialog} this
28729          * @param {Roo.EventObject} e
28730          */
28731         "keydown" : true,
28732         /**
28733          * @event move
28734          * Fires when this dialog is moved by the user.
28735          * @param {Roo.BasicDialog} this
28736          * @param {Number} x The new page X
28737          * @param {Number} y The new page Y
28738          */
28739         "move" : true,
28740         /**
28741          * @event resize
28742          * Fires when this dialog is resized by the user.
28743          * @param {Roo.BasicDialog} this
28744          * @param {Number} width The new width
28745          * @param {Number} height The new height
28746          */
28747         "resize" : true,
28748         /**
28749          * @event beforehide
28750          * Fires before this dialog is hidden.
28751          * @param {Roo.BasicDialog} this
28752          */
28753         "beforehide" : true,
28754         /**
28755          * @event hide
28756          * Fires when this dialog is hidden.
28757          * @param {Roo.BasicDialog} this
28758          */
28759         "hide" : true,
28760         /**
28761          * @event beforeshow
28762          * Fires before this dialog is shown.
28763          * @param {Roo.BasicDialog} this
28764          */
28765         "beforeshow" : true,
28766         /**
28767          * @event show
28768          * Fires when this dialog is shown.
28769          * @param {Roo.BasicDialog} this
28770          */
28771         "show" : true
28772     });
28773     el.on("keydown", this.onKeyDown, this);
28774     el.on("mousedown", this.toFront, this);
28775     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28776     this.el.hide();
28777     Roo.DialogManager.register(this);
28778     Roo.BasicDialog.superclass.constructor.call(this);
28779 };
28780
28781 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28782     shadowOffset: Roo.isIE ? 6 : 5,
28783     minHeight: 80,
28784     minWidth: 200,
28785     minButtonWidth: 75,
28786     defaultButton: null,
28787     buttonAlign: "right",
28788     tabTag: 'div',
28789     firstShow: true,
28790
28791     /**
28792      * Sets the dialog title text
28793      * @param {String} text The title text to display
28794      * @return {Roo.BasicDialog} this
28795      */
28796     setTitle : function(text){
28797         this.header.update(text);
28798         return this;
28799     },
28800
28801     // private
28802     closeClick : function(){
28803         this.hide();
28804     },
28805
28806     // private
28807     collapseClick : function(){
28808         this[this.collapsed ? "expand" : "collapse"]();
28809     },
28810
28811     /**
28812      * Collapses the dialog to its minimized state (only the title bar is visible).
28813      * Equivalent to the user clicking the collapse dialog button.
28814      */
28815     collapse : function(){
28816         if(!this.collapsed){
28817             this.collapsed = true;
28818             this.el.addClass("x-dlg-collapsed");
28819             this.restoreHeight = this.el.getHeight();
28820             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28821         }
28822     },
28823
28824     /**
28825      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28826      * clicking the expand dialog button.
28827      */
28828     expand : function(){
28829         if(this.collapsed){
28830             this.collapsed = false;
28831             this.el.removeClass("x-dlg-collapsed");
28832             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28833         }
28834     },
28835
28836     /**
28837      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28838      * @return {Roo.TabPanel} The tabs component
28839      */
28840     initTabs : function(){
28841         var tabs = this.getTabs();
28842         while(tabs.getTab(0)){
28843             tabs.removeTab(0);
28844         }
28845         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28846             var dom = el.dom;
28847             tabs.addTab(Roo.id(dom), dom.title);
28848             dom.title = "";
28849         });
28850         tabs.activate(0);
28851         return tabs;
28852     },
28853
28854     // private
28855     beforeResize : function(){
28856         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28857     },
28858
28859     // private
28860     onResize : function(){
28861         this.refreshSize();
28862         this.syncBodyHeight();
28863         this.adjustAssets();
28864         this.focus();
28865         this.fireEvent("resize", this, this.size.width, this.size.height);
28866     },
28867
28868     // private
28869     onKeyDown : function(e){
28870         if(this.isVisible()){
28871             this.fireEvent("keydown", this, e);
28872         }
28873     },
28874
28875     /**
28876      * Resizes the dialog.
28877      * @param {Number} width
28878      * @param {Number} height
28879      * @return {Roo.BasicDialog} this
28880      */
28881     resizeTo : function(width, height){
28882         this.el.setSize(width, height);
28883         this.size = {width: width, height: height};
28884         this.syncBodyHeight();
28885         if(this.fixedcenter){
28886             this.center();
28887         }
28888         if(this.isVisible()){
28889             this.constrainXY();
28890             this.adjustAssets();
28891         }
28892         this.fireEvent("resize", this, width, height);
28893         return this;
28894     },
28895
28896
28897     /**
28898      * Resizes the dialog to fit the specified content size.
28899      * @param {Number} width
28900      * @param {Number} height
28901      * @return {Roo.BasicDialog} this
28902      */
28903     setContentSize : function(w, h){
28904         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28905         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28906         //if(!this.el.isBorderBox()){
28907             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28908             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28909         //}
28910         if(this.tabs){
28911             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28912             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28913         }
28914         this.resizeTo(w, h);
28915         return this;
28916     },
28917
28918     /**
28919      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28920      * executed in response to a particular key being pressed while the dialog is active.
28921      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28922      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28923      * @param {Function} fn The function to call
28924      * @param {Object} scope (optional) The scope of the function
28925      * @return {Roo.BasicDialog} this
28926      */
28927     addKeyListener : function(key, fn, scope){
28928         var keyCode, shift, ctrl, alt;
28929         if(typeof key == "object" && !(key instanceof Array)){
28930             keyCode = key["key"];
28931             shift = key["shift"];
28932             ctrl = key["ctrl"];
28933             alt = key["alt"];
28934         }else{
28935             keyCode = key;
28936         }
28937         var handler = function(dlg, e){
28938             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28939                 var k = e.getKey();
28940                 if(keyCode instanceof Array){
28941                     for(var i = 0, len = keyCode.length; i < len; i++){
28942                         if(keyCode[i] == k){
28943                           fn.call(scope || window, dlg, k, e);
28944                           return;
28945                         }
28946                     }
28947                 }else{
28948                     if(k == keyCode){
28949                         fn.call(scope || window, dlg, k, e);
28950                     }
28951                 }
28952             }
28953         };
28954         this.on("keydown", handler);
28955         return this;
28956     },
28957
28958     /**
28959      * Returns the TabPanel component (creates it if it doesn't exist).
28960      * Note: If you wish to simply check for the existence of tabs without creating them,
28961      * check for a null 'tabs' property.
28962      * @return {Roo.TabPanel} The tabs component
28963      */
28964     getTabs : function(){
28965         if(!this.tabs){
28966             this.el.addClass("x-dlg-auto-tabs");
28967             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28968             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28969         }
28970         return this.tabs;
28971     },
28972
28973     /**
28974      * Adds a button to the footer section of the dialog.
28975      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28976      * object or a valid Roo.DomHelper element config
28977      * @param {Function} handler The function called when the button is clicked
28978      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28979      * @return {Roo.Button} The new button
28980      */
28981     addButton : function(config, handler, scope){
28982         var dh = Roo.DomHelper;
28983         if(!this.footer){
28984             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28985         }
28986         if(!this.btnContainer){
28987             var tb = this.footer.createChild({
28988
28989                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28990                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28991             }, null, true);
28992             this.btnContainer = tb.firstChild.firstChild.firstChild;
28993         }
28994         var bconfig = {
28995             handler: handler,
28996             scope: scope,
28997             minWidth: this.minButtonWidth,
28998             hideParent:true
28999         };
29000         if(typeof config == "string"){
29001             bconfig.text = config;
29002         }else{
29003             if(config.tag){
29004                 bconfig.dhconfig = config;
29005             }else{
29006                 Roo.apply(bconfig, config);
29007             }
29008         }
29009         var fc = false;
29010         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29011             bconfig.position = Math.max(0, bconfig.position);
29012             fc = this.btnContainer.childNodes[bconfig.position];
29013         }
29014          
29015         var btn = new Roo.Button(
29016             fc ? 
29017                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29018                 : this.btnContainer.appendChild(document.createElement("td")),
29019             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29020             bconfig
29021         );
29022         this.syncBodyHeight();
29023         if(!this.buttons){
29024             /**
29025              * Array of all the buttons that have been added to this dialog via addButton
29026              * @type Array
29027              */
29028             this.buttons = [];
29029         }
29030         this.buttons.push(btn);
29031         return btn;
29032     },
29033
29034     /**
29035      * Sets the default button to be focused when the dialog is displayed.
29036      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29037      * @return {Roo.BasicDialog} this
29038      */
29039     setDefaultButton : function(btn){
29040         this.defaultButton = btn;
29041         return this;
29042     },
29043
29044     // private
29045     getHeaderFooterHeight : function(safe){
29046         var height = 0;
29047         if(this.header){
29048            height += this.header.getHeight();
29049         }
29050         if(this.footer){
29051            var fm = this.footer.getMargins();
29052             height += (this.footer.getHeight()+fm.top+fm.bottom);
29053         }
29054         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29055         height += this.centerBg.getPadding("tb");
29056         return height;
29057     },
29058
29059     // private
29060     syncBodyHeight : function(){
29061         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29062         var height = this.size.height - this.getHeaderFooterHeight(false);
29063         bd.setHeight(height-bd.getMargins("tb"));
29064         var hh = this.header.getHeight();
29065         var h = this.size.height-hh;
29066         cb.setHeight(h);
29067         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29068         bw.setHeight(h-cb.getPadding("tb"));
29069         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29070         bd.setWidth(bw.getWidth(true));
29071         if(this.tabs){
29072             this.tabs.syncHeight();
29073             if(Roo.isIE){
29074                 this.tabs.el.repaint();
29075             }
29076         }
29077     },
29078
29079     /**
29080      * Restores the previous state of the dialog if Roo.state is configured.
29081      * @return {Roo.BasicDialog} this
29082      */
29083     restoreState : function(){
29084         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29085         if(box && box.width){
29086             this.xy = [box.x, box.y];
29087             this.resizeTo(box.width, box.height);
29088         }
29089         return this;
29090     },
29091
29092     // private
29093     beforeShow : function(){
29094         this.expand();
29095         if(this.fixedcenter){
29096             this.xy = this.el.getCenterXY(true);
29097         }
29098         if(this.modal){
29099             Roo.get(document.body).addClass("x-body-masked");
29100             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29101             this.mask.show();
29102         }
29103         this.constrainXY();
29104     },
29105
29106     // private
29107     animShow : function(){
29108         var b = Roo.get(this.animateTarget).getBox();
29109         this.proxy.setSize(b.width, b.height);
29110         this.proxy.setLocation(b.x, b.y);
29111         this.proxy.show();
29112         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29113                     true, .35, this.showEl.createDelegate(this));
29114     },
29115
29116     /**
29117      * Shows the dialog.
29118      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29119      * @return {Roo.BasicDialog} this
29120      */
29121     show : function(animateTarget){
29122         if (this.fireEvent("beforeshow", this) === false){
29123             return;
29124         }
29125         if(this.syncHeightBeforeShow){
29126             this.syncBodyHeight();
29127         }else if(this.firstShow){
29128             this.firstShow = false;
29129             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29130         }
29131         this.animateTarget = animateTarget || this.animateTarget;
29132         if(!this.el.isVisible()){
29133             this.beforeShow();
29134             if(this.animateTarget && Roo.get(this.animateTarget)){
29135                 this.animShow();
29136             }else{
29137                 this.showEl();
29138             }
29139         }
29140         return this;
29141     },
29142
29143     // private
29144     showEl : function(){
29145         this.proxy.hide();
29146         this.el.setXY(this.xy);
29147         this.el.show();
29148         this.adjustAssets(true);
29149         this.toFront();
29150         this.focus();
29151         // IE peekaboo bug - fix found by Dave Fenwick
29152         if(Roo.isIE){
29153             this.el.repaint();
29154         }
29155         this.fireEvent("show", this);
29156     },
29157
29158     /**
29159      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29160      * dialog itself will receive focus.
29161      */
29162     focus : function(){
29163         if(this.defaultButton){
29164             this.defaultButton.focus();
29165         }else{
29166             this.focusEl.focus();
29167         }
29168     },
29169
29170     // private
29171     constrainXY : function(){
29172         if(this.constraintoviewport !== false){
29173             if(!this.viewSize){
29174                 if(this.container){
29175                     var s = this.container.getSize();
29176                     this.viewSize = [s.width, s.height];
29177                 }else{
29178                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29179                 }
29180             }
29181             var s = Roo.get(this.container||document).getScroll();
29182
29183             var x = this.xy[0], y = this.xy[1];
29184             var w = this.size.width, h = this.size.height;
29185             var vw = this.viewSize[0], vh = this.viewSize[1];
29186             // only move it if it needs it
29187             var moved = false;
29188             // first validate right/bottom
29189             if(x + w > vw+s.left){
29190                 x = vw - w;
29191                 moved = true;
29192             }
29193             if(y + h > vh+s.top){
29194                 y = vh - h;
29195                 moved = true;
29196             }
29197             // then make sure top/left isn't negative
29198             if(x < s.left){
29199                 x = s.left;
29200                 moved = true;
29201             }
29202             if(y < s.top){
29203                 y = s.top;
29204                 moved = true;
29205             }
29206             if(moved){
29207                 // cache xy
29208                 this.xy = [x, y];
29209                 if(this.isVisible()){
29210                     this.el.setLocation(x, y);
29211                     this.adjustAssets();
29212                 }
29213             }
29214         }
29215     },
29216
29217     // private
29218     onDrag : function(){
29219         if(!this.proxyDrag){
29220             this.xy = this.el.getXY();
29221             this.adjustAssets();
29222         }
29223     },
29224
29225     // private
29226     adjustAssets : function(doShow){
29227         var x = this.xy[0], y = this.xy[1];
29228         var w = this.size.width, h = this.size.height;
29229         if(doShow === true){
29230             if(this.shadow){
29231                 this.shadow.show(this.el);
29232             }
29233             if(this.shim){
29234                 this.shim.show();
29235             }
29236         }
29237         if(this.shadow && this.shadow.isVisible()){
29238             this.shadow.show(this.el);
29239         }
29240         if(this.shim && this.shim.isVisible()){
29241             this.shim.setBounds(x, y, w, h);
29242         }
29243     },
29244
29245     // private
29246     adjustViewport : function(w, h){
29247         if(!w || !h){
29248             w = Roo.lib.Dom.getViewWidth();
29249             h = Roo.lib.Dom.getViewHeight();
29250         }
29251         // cache the size
29252         this.viewSize = [w, h];
29253         if(this.modal && this.mask.isVisible()){
29254             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29255             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29256         }
29257         if(this.isVisible()){
29258             this.constrainXY();
29259         }
29260     },
29261
29262     /**
29263      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29264      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29265      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29266      */
29267     destroy : function(removeEl){
29268         if(this.isVisible()){
29269             this.animateTarget = null;
29270             this.hide();
29271         }
29272         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29273         if(this.tabs){
29274             this.tabs.destroy(removeEl);
29275         }
29276         Roo.destroy(
29277              this.shim,
29278              this.proxy,
29279              this.resizer,
29280              this.close,
29281              this.mask
29282         );
29283         if(this.dd){
29284             this.dd.unreg();
29285         }
29286         if(this.buttons){
29287            for(var i = 0, len = this.buttons.length; i < len; i++){
29288                this.buttons[i].destroy();
29289            }
29290         }
29291         this.el.removeAllListeners();
29292         if(removeEl === true){
29293             this.el.update("");
29294             this.el.remove();
29295         }
29296         Roo.DialogManager.unregister(this);
29297     },
29298
29299     // private
29300     startMove : function(){
29301         if(this.proxyDrag){
29302             this.proxy.show();
29303         }
29304         if(this.constraintoviewport !== false){
29305             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29306         }
29307     },
29308
29309     // private
29310     endMove : function(){
29311         if(!this.proxyDrag){
29312             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29313         }else{
29314             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29315             this.proxy.hide();
29316         }
29317         this.refreshSize();
29318         this.adjustAssets();
29319         this.focus();
29320         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29321     },
29322
29323     /**
29324      * Brings this dialog to the front of any other visible dialogs
29325      * @return {Roo.BasicDialog} this
29326      */
29327     toFront : function(){
29328         Roo.DialogManager.bringToFront(this);
29329         return this;
29330     },
29331
29332     /**
29333      * Sends this dialog to the back (under) of any other visible dialogs
29334      * @return {Roo.BasicDialog} this
29335      */
29336     toBack : function(){
29337         Roo.DialogManager.sendToBack(this);
29338         return this;
29339     },
29340
29341     /**
29342      * Centers this dialog in the viewport
29343      * @return {Roo.BasicDialog} this
29344      */
29345     center : function(){
29346         var xy = this.el.getCenterXY(true);
29347         this.moveTo(xy[0], xy[1]);
29348         return this;
29349     },
29350
29351     /**
29352      * Moves the dialog's top-left corner to the specified point
29353      * @param {Number} x
29354      * @param {Number} y
29355      * @return {Roo.BasicDialog} this
29356      */
29357     moveTo : function(x, y){
29358         this.xy = [x,y];
29359         if(this.isVisible()){
29360             this.el.setXY(this.xy);
29361             this.adjustAssets();
29362         }
29363         return this;
29364     },
29365
29366     /**
29367      * Aligns the dialog to the specified element
29368      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29369      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29370      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29371      * @return {Roo.BasicDialog} this
29372      */
29373     alignTo : function(element, position, offsets){
29374         this.xy = this.el.getAlignToXY(element, position, offsets);
29375         if(this.isVisible()){
29376             this.el.setXY(this.xy);
29377             this.adjustAssets();
29378         }
29379         return this;
29380     },
29381
29382     /**
29383      * Anchors an element to another element and realigns it when the window is resized.
29384      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29385      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29386      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29387      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29388      * is a number, it is used as the buffer delay (defaults to 50ms).
29389      * @return {Roo.BasicDialog} this
29390      */
29391     anchorTo : function(el, alignment, offsets, monitorScroll){
29392         var action = function(){
29393             this.alignTo(el, alignment, offsets);
29394         };
29395         Roo.EventManager.onWindowResize(action, this);
29396         var tm = typeof monitorScroll;
29397         if(tm != 'undefined'){
29398             Roo.EventManager.on(window, 'scroll', action, this,
29399                 {buffer: tm == 'number' ? monitorScroll : 50});
29400         }
29401         action.call(this);
29402         return this;
29403     },
29404
29405     /**
29406      * Returns true if the dialog is visible
29407      * @return {Boolean}
29408      */
29409     isVisible : function(){
29410         return this.el.isVisible();
29411     },
29412
29413     // private
29414     animHide : function(callback){
29415         var b = Roo.get(this.animateTarget).getBox();
29416         this.proxy.show();
29417         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29418         this.el.hide();
29419         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29420                     this.hideEl.createDelegate(this, [callback]));
29421     },
29422
29423     /**
29424      * Hides the dialog.
29425      * @param {Function} callback (optional) Function to call when the dialog is hidden
29426      * @return {Roo.BasicDialog} this
29427      */
29428     hide : function(callback){
29429         if (this.fireEvent("beforehide", this) === false){
29430             return;
29431         }
29432         if(this.shadow){
29433             this.shadow.hide();
29434         }
29435         if(this.shim) {
29436           this.shim.hide();
29437         }
29438         // sometimes animateTarget seems to get set.. causing problems...
29439         // this just double checks..
29440         if(this.animateTarget && Roo.get(this.animateTarget)) {
29441            this.animHide(callback);
29442         }else{
29443             this.el.hide();
29444             this.hideEl(callback);
29445         }
29446         return this;
29447     },
29448
29449     // private
29450     hideEl : function(callback){
29451         this.proxy.hide();
29452         if(this.modal){
29453             this.mask.hide();
29454             Roo.get(document.body).removeClass("x-body-masked");
29455         }
29456         this.fireEvent("hide", this);
29457         if(typeof callback == "function"){
29458             callback();
29459         }
29460     },
29461
29462     // private
29463     hideAction : function(){
29464         this.setLeft("-10000px");
29465         this.setTop("-10000px");
29466         this.setStyle("visibility", "hidden");
29467     },
29468
29469     // private
29470     refreshSize : function(){
29471         this.size = this.el.getSize();
29472         this.xy = this.el.getXY();
29473         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29474     },
29475
29476     // private
29477     // z-index is managed by the DialogManager and may be overwritten at any time
29478     setZIndex : function(index){
29479         if(this.modal){
29480             this.mask.setStyle("z-index", index);
29481         }
29482         if(this.shim){
29483             this.shim.setStyle("z-index", ++index);
29484         }
29485         if(this.shadow){
29486             this.shadow.setZIndex(++index);
29487         }
29488         this.el.setStyle("z-index", ++index);
29489         if(this.proxy){
29490             this.proxy.setStyle("z-index", ++index);
29491         }
29492         if(this.resizer){
29493             this.resizer.proxy.setStyle("z-index", ++index);
29494         }
29495
29496         this.lastZIndex = index;
29497     },
29498
29499     /**
29500      * Returns the element for this dialog
29501      * @return {Roo.Element} The underlying dialog Element
29502      */
29503     getEl : function(){
29504         return this.el;
29505     }
29506 });
29507
29508 /**
29509  * @class Roo.DialogManager
29510  * Provides global access to BasicDialogs that have been created and
29511  * support for z-indexing (layering) multiple open dialogs.
29512  */
29513 Roo.DialogManager = function(){
29514     var list = {};
29515     var accessList = [];
29516     var front = null;
29517
29518     // private
29519     var sortDialogs = function(d1, d2){
29520         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29521     };
29522
29523     // private
29524     var orderDialogs = function(){
29525         accessList.sort(sortDialogs);
29526         var seed = Roo.DialogManager.zseed;
29527         for(var i = 0, len = accessList.length; i < len; i++){
29528             var dlg = accessList[i];
29529             if(dlg){
29530                 dlg.setZIndex(seed + (i*10));
29531             }
29532         }
29533     };
29534
29535     return {
29536         /**
29537          * The starting z-index for BasicDialogs (defaults to 9000)
29538          * @type Number The z-index value
29539          */
29540         zseed : 9000,
29541
29542         // private
29543         register : function(dlg){
29544             list[dlg.id] = dlg;
29545             accessList.push(dlg);
29546         },
29547
29548         // private
29549         unregister : function(dlg){
29550             delete list[dlg.id];
29551             var i=0;
29552             var len=0;
29553             if(!accessList.indexOf){
29554                 for(  i = 0, len = accessList.length; i < len; i++){
29555                     if(accessList[i] == dlg){
29556                         accessList.splice(i, 1);
29557                         return;
29558                     }
29559                 }
29560             }else{
29561                  i = accessList.indexOf(dlg);
29562                 if(i != -1){
29563                     accessList.splice(i, 1);
29564                 }
29565             }
29566         },
29567
29568         /**
29569          * Gets a registered dialog by id
29570          * @param {String/Object} id The id of the dialog or a dialog
29571          * @return {Roo.BasicDialog} this
29572          */
29573         get : function(id){
29574             return typeof id == "object" ? id : list[id];
29575         },
29576
29577         /**
29578          * Brings the specified dialog to the front
29579          * @param {String/Object} dlg The id of the dialog or a dialog
29580          * @return {Roo.BasicDialog} this
29581          */
29582         bringToFront : function(dlg){
29583             dlg = this.get(dlg);
29584             if(dlg != front){
29585                 front = dlg;
29586                 dlg._lastAccess = new Date().getTime();
29587                 orderDialogs();
29588             }
29589             return dlg;
29590         },
29591
29592         /**
29593          * Sends the specified dialog to the back
29594          * @param {String/Object} dlg The id of the dialog or a dialog
29595          * @return {Roo.BasicDialog} this
29596          */
29597         sendToBack : function(dlg){
29598             dlg = this.get(dlg);
29599             dlg._lastAccess = -(new Date().getTime());
29600             orderDialogs();
29601             return dlg;
29602         },
29603
29604         /**
29605          * Hides all dialogs
29606          */
29607         hideAll : function(){
29608             for(var id in list){
29609                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29610                     list[id].hide();
29611                 }
29612             }
29613         }
29614     };
29615 }();
29616
29617 /**
29618  * @class Roo.LayoutDialog
29619  * @extends Roo.BasicDialog
29620  * Dialog which provides adjustments for working with a layout in a Dialog.
29621  * Add your necessary layout config options to the dialog's config.<br>
29622  * Example usage (including a nested layout):
29623  * <pre><code>
29624 if(!dialog){
29625     dialog = new Roo.LayoutDialog("download-dlg", {
29626         modal: true,
29627         width:600,
29628         height:450,
29629         shadow:true,
29630         minWidth:500,
29631         minHeight:350,
29632         autoTabs:true,
29633         proxyDrag:true,
29634         // layout config merges with the dialog config
29635         center:{
29636             tabPosition: "top",
29637             alwaysShowTabs: true
29638         }
29639     });
29640     dialog.addKeyListener(27, dialog.hide, dialog);
29641     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29642     dialog.addButton("Build It!", this.getDownload, this);
29643
29644     // we can even add nested layouts
29645     var innerLayout = new Roo.BorderLayout("dl-inner", {
29646         east: {
29647             initialSize: 200,
29648             autoScroll:true,
29649             split:true
29650         },
29651         center: {
29652             autoScroll:true
29653         }
29654     });
29655     innerLayout.beginUpdate();
29656     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29657     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29658     innerLayout.endUpdate(true);
29659
29660     var layout = dialog.getLayout();
29661     layout.beginUpdate();
29662     layout.add("center", new Roo.ContentPanel("standard-panel",
29663                         {title: "Download the Source", fitToFrame:true}));
29664     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29665                {title: "Build your own roo.js"}));
29666     layout.getRegion("center").showPanel(sp);
29667     layout.endUpdate();
29668 }
29669 </code></pre>
29670     * @constructor
29671     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29672     * @param {Object} config configuration options
29673   */
29674 Roo.LayoutDialog = function(el, cfg){
29675     
29676     var config=  cfg;
29677     if (typeof(cfg) == 'undefined') {
29678         config = Roo.apply({}, el);
29679         // not sure why we use documentElement here.. - it should always be body.
29680         // IE7 borks horribly if we use documentElement.
29681         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29682         //config.autoCreate = true;
29683     }
29684     
29685     
29686     config.autoTabs = false;
29687     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29688     this.body.setStyle({overflow:"hidden", position:"relative"});
29689     this.layout = new Roo.BorderLayout(this.body.dom, config);
29690     this.layout.monitorWindowResize = false;
29691     this.el.addClass("x-dlg-auto-layout");
29692     // fix case when center region overwrites center function
29693     this.center = Roo.BasicDialog.prototype.center;
29694     this.on("show", this.layout.layout, this.layout, true);
29695     if (config.items) {
29696         var xitems = config.items;
29697         delete config.items;
29698         Roo.each(xitems, this.addxtype, this);
29699     }
29700     
29701     
29702 };
29703 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29704     /**
29705      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29706      * @deprecated
29707      */
29708     endUpdate : function(){
29709         this.layout.endUpdate();
29710     },
29711
29712     /**
29713      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29714      *  @deprecated
29715      */
29716     beginUpdate : function(){
29717         this.layout.beginUpdate();
29718     },
29719
29720     /**
29721      * Get the BorderLayout for this dialog
29722      * @return {Roo.BorderLayout}
29723      */
29724     getLayout : function(){
29725         return this.layout;
29726     },
29727
29728     showEl : function(){
29729         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29730         if(Roo.isIE7){
29731             this.layout.layout();
29732         }
29733     },
29734
29735     // private
29736     // Use the syncHeightBeforeShow config option to control this automatically
29737     syncBodyHeight : function(){
29738         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29739         if(this.layout){this.layout.layout();}
29740     },
29741     
29742       /**
29743      * Add an xtype element (actually adds to the layout.)
29744      * @return {Object} xdata xtype object data.
29745      */
29746     
29747     addxtype : function(c) {
29748         return this.layout.addxtype(c);
29749     }
29750 });/*
29751  * Based on:
29752  * Ext JS Library 1.1.1
29753  * Copyright(c) 2006-2007, Ext JS, LLC.
29754  *
29755  * Originally Released Under LGPL - original licence link has changed is not relivant.
29756  *
29757  * Fork - LGPL
29758  * <script type="text/javascript">
29759  */
29760  
29761 /**
29762  * @class Roo.MessageBox
29763  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29764  * Example usage:
29765  *<pre><code>
29766 // Basic alert:
29767 Roo.Msg.alert('Status', 'Changes saved successfully.');
29768
29769 // Prompt for user data:
29770 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29771     if (btn == 'ok'){
29772         // process text value...
29773     }
29774 });
29775
29776 // Show a dialog using config options:
29777 Roo.Msg.show({
29778    title:'Save Changes?',
29779    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29780    buttons: Roo.Msg.YESNOCANCEL,
29781    fn: processResult,
29782    animEl: 'elId'
29783 });
29784 </code></pre>
29785  * @singleton
29786  */
29787 Roo.MessageBox = function(){
29788     var dlg, opt, mask, waitTimer;
29789     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29790     var buttons, activeTextEl, bwidth;
29791
29792     // private
29793     var handleButton = function(button){
29794         dlg.hide();
29795         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29796     };
29797
29798     // private
29799     var handleHide = function(){
29800         if(opt && opt.cls){
29801             dlg.el.removeClass(opt.cls);
29802         }
29803         if(waitTimer){
29804             Roo.TaskMgr.stop(waitTimer);
29805             waitTimer = null;
29806         }
29807     };
29808
29809     // private
29810     var updateButtons = function(b){
29811         var width = 0;
29812         if(!b){
29813             buttons["ok"].hide();
29814             buttons["cancel"].hide();
29815             buttons["yes"].hide();
29816             buttons["no"].hide();
29817             dlg.footer.dom.style.display = 'none';
29818             return width;
29819         }
29820         dlg.footer.dom.style.display = '';
29821         for(var k in buttons){
29822             if(typeof buttons[k] != "function"){
29823                 if(b[k]){
29824                     buttons[k].show();
29825                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29826                     width += buttons[k].el.getWidth()+15;
29827                 }else{
29828                     buttons[k].hide();
29829                 }
29830             }
29831         }
29832         return width;
29833     };
29834
29835     // private
29836     var handleEsc = function(d, k, e){
29837         if(opt && opt.closable !== false){
29838             dlg.hide();
29839         }
29840         if(e){
29841             e.stopEvent();
29842         }
29843     };
29844
29845     return {
29846         /**
29847          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29848          * @return {Roo.BasicDialog} The BasicDialog element
29849          */
29850         getDialog : function(){
29851            if(!dlg){
29852                 dlg = new Roo.BasicDialog("x-msg-box", {
29853                     autoCreate : true,
29854                     shadow: true,
29855                     draggable: true,
29856                     resizable:false,
29857                     constraintoviewport:false,
29858                     fixedcenter:true,
29859                     collapsible : false,
29860                     shim:true,
29861                     modal: true,
29862                     width:400, height:100,
29863                     buttonAlign:"center",
29864                     closeClick : function(){
29865                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29866                             handleButton("no");
29867                         }else{
29868                             handleButton("cancel");
29869                         }
29870                     }
29871                 });
29872                 dlg.on("hide", handleHide);
29873                 mask = dlg.mask;
29874                 dlg.addKeyListener(27, handleEsc);
29875                 buttons = {};
29876                 var bt = this.buttonText;
29877                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29878                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29879                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29880                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29881                 bodyEl = dlg.body.createChild({
29882
29883                     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>'
29884                 });
29885                 msgEl = bodyEl.dom.firstChild;
29886                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29887                 textboxEl.enableDisplayMode();
29888                 textboxEl.addKeyListener([10,13], function(){
29889                     if(dlg.isVisible() && opt && opt.buttons){
29890                         if(opt.buttons.ok){
29891                             handleButton("ok");
29892                         }else if(opt.buttons.yes){
29893                             handleButton("yes");
29894                         }
29895                     }
29896                 });
29897                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29898                 textareaEl.enableDisplayMode();
29899                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29900                 progressEl.enableDisplayMode();
29901                 var pf = progressEl.dom.firstChild;
29902                 if (pf) {
29903                     pp = Roo.get(pf.firstChild);
29904                     pp.setHeight(pf.offsetHeight);
29905                 }
29906                 
29907             }
29908             return dlg;
29909         },
29910
29911         /**
29912          * Updates the message box body text
29913          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29914          * the XHTML-compliant non-breaking space character '&amp;#160;')
29915          * @return {Roo.MessageBox} This message box
29916          */
29917         updateText : function(text){
29918             if(!dlg.isVisible() && !opt.width){
29919                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29920             }
29921             msgEl.innerHTML = text || '&#160;';
29922             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29923                         Math.max(opt.minWidth || this.minWidth, bwidth));
29924             if(opt.prompt){
29925                 activeTextEl.setWidth(w);
29926             }
29927             if(dlg.isVisible()){
29928                 dlg.fixedcenter = false;
29929             }
29930             dlg.setContentSize(w, bodyEl.getHeight());
29931             if(dlg.isVisible()){
29932                 dlg.fixedcenter = true;
29933             }
29934             return this;
29935         },
29936
29937         /**
29938          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29939          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29940          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29941          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29942          * @return {Roo.MessageBox} This message box
29943          */
29944         updateProgress : function(value, text){
29945             if(text){
29946                 this.updateText(text);
29947             }
29948             if (pp) { // weird bug on my firefox - for some reason this is not defined
29949                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29950             }
29951             return this;
29952         },        
29953
29954         /**
29955          * Returns true if the message box is currently displayed
29956          * @return {Boolean} True if the message box is visible, else false
29957          */
29958         isVisible : function(){
29959             return dlg && dlg.isVisible();  
29960         },
29961
29962         /**
29963          * Hides the message box if it is displayed
29964          */
29965         hide : function(){
29966             if(this.isVisible()){
29967                 dlg.hide();
29968             }  
29969         },
29970
29971         /**
29972          * Displays a new message box, or reinitializes an existing message box, based on the config options
29973          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29974          * The following config object properties are supported:
29975          * <pre>
29976 Property    Type             Description
29977 ----------  ---------------  ------------------------------------------------------------------------------------
29978 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29979                                    closes (defaults to undefined)
29980 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29981                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29982 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29983                                    progress and wait dialogs will ignore this property and always hide the
29984                                    close button as they can only be closed programmatically.
29985 cls               String           A custom CSS class to apply to the message box element
29986 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29987                                    displayed (defaults to 75)
29988 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29989                                    function will be btn (the name of the button that was clicked, if applicable,
29990                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29991                                    Progress and wait dialogs will ignore this option since they do not respond to
29992                                    user actions and can only be closed programmatically, so any required function
29993                                    should be called by the same code after it closes the dialog.
29994 icon              String           A CSS class that provides a background image to be used as an icon for
29995                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29996 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29997 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29998 modal             Boolean          False to allow user interaction with the page while the message box is
29999                                    displayed (defaults to true)
30000 msg               String           A string that will replace the existing message box body text (defaults
30001                                    to the XHTML-compliant non-breaking space character '&#160;')
30002 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30003 progress          Boolean          True to display a progress bar (defaults to false)
30004 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30005 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30006 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30007 title             String           The title text
30008 value             String           The string value to set into the active textbox element if displayed
30009 wait              Boolean          True to display a progress bar (defaults to false)
30010 width             Number           The width of the dialog in pixels
30011 </pre>
30012          *
30013          * Example usage:
30014          * <pre><code>
30015 Roo.Msg.show({
30016    title: 'Address',
30017    msg: 'Please enter your address:',
30018    width: 300,
30019    buttons: Roo.MessageBox.OKCANCEL,
30020    multiline: true,
30021    fn: saveAddress,
30022    animEl: 'addAddressBtn'
30023 });
30024 </code></pre>
30025          * @param {Object} config Configuration options
30026          * @return {Roo.MessageBox} This message box
30027          */
30028         show : function(options){
30029             if(this.isVisible()){
30030                 this.hide();
30031             }
30032             var d = this.getDialog();
30033             opt = options;
30034             d.setTitle(opt.title || "&#160;");
30035             d.close.setDisplayed(opt.closable !== false);
30036             activeTextEl = textboxEl;
30037             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30038             if(opt.prompt){
30039                 if(opt.multiline){
30040                     textboxEl.hide();
30041                     textareaEl.show();
30042                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30043                         opt.multiline : this.defaultTextHeight);
30044                     activeTextEl = textareaEl;
30045                 }else{
30046                     textboxEl.show();
30047                     textareaEl.hide();
30048                 }
30049             }else{
30050                 textboxEl.hide();
30051                 textareaEl.hide();
30052             }
30053             progressEl.setDisplayed(opt.progress === true);
30054             this.updateProgress(0);
30055             activeTextEl.dom.value = opt.value || "";
30056             if(opt.prompt){
30057                 dlg.setDefaultButton(activeTextEl);
30058             }else{
30059                 var bs = opt.buttons;
30060                 var db = null;
30061                 if(bs && bs.ok){
30062                     db = buttons["ok"];
30063                 }else if(bs && bs.yes){
30064                     db = buttons["yes"];
30065                 }
30066                 dlg.setDefaultButton(db);
30067             }
30068             bwidth = updateButtons(opt.buttons);
30069             this.updateText(opt.msg);
30070             if(opt.cls){
30071                 d.el.addClass(opt.cls);
30072             }
30073             d.proxyDrag = opt.proxyDrag === true;
30074             d.modal = opt.modal !== false;
30075             d.mask = opt.modal !== false ? mask : false;
30076             if(!d.isVisible()){
30077                 // force it to the end of the z-index stack so it gets a cursor in FF
30078                 document.body.appendChild(dlg.el.dom);
30079                 d.animateTarget = null;
30080                 d.show(options.animEl);
30081             }
30082             return this;
30083         },
30084
30085         /**
30086          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30087          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30088          * and closing the message box when the process is complete.
30089          * @param {String} title The title bar text
30090          * @param {String} msg The message box body text
30091          * @return {Roo.MessageBox} This message box
30092          */
30093         progress : function(title, msg){
30094             this.show({
30095                 title : title,
30096                 msg : msg,
30097                 buttons: false,
30098                 progress:true,
30099                 closable:false,
30100                 minWidth: this.minProgressWidth,
30101                 modal : true
30102             });
30103             return this;
30104         },
30105
30106         /**
30107          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30108          * If a callback function is passed it will be called after the user clicks the button, and the
30109          * id of the button that was clicked will be passed as the only parameter to the callback
30110          * (could also be the top-right close button).
30111          * @param {String} title The title bar text
30112          * @param {String} msg The message box body text
30113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30114          * @param {Object} scope (optional) The scope of the callback function
30115          * @return {Roo.MessageBox} This message box
30116          */
30117         alert : function(title, msg, fn, scope){
30118             this.show({
30119                 title : title,
30120                 msg : msg,
30121                 buttons: this.OK,
30122                 fn: fn,
30123                 scope : scope,
30124                 modal : true
30125             });
30126             return this;
30127         },
30128
30129         /**
30130          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30131          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30132          * You are responsible for closing the message box when the process is complete.
30133          * @param {String} msg The message box body text
30134          * @param {String} title (optional) The title bar text
30135          * @return {Roo.MessageBox} This message box
30136          */
30137         wait : function(msg, title){
30138             this.show({
30139                 title : title,
30140                 msg : msg,
30141                 buttons: false,
30142                 closable:false,
30143                 progress:true,
30144                 modal:true,
30145                 width:300,
30146                 wait:true
30147             });
30148             waitTimer = Roo.TaskMgr.start({
30149                 run: function(i){
30150                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30151                 },
30152                 interval: 1000
30153             });
30154             return this;
30155         },
30156
30157         /**
30158          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30159          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30160          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30161          * @param {String} title The title bar text
30162          * @param {String} msg The message box body text
30163          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30164          * @param {Object} scope (optional) The scope of the callback function
30165          * @return {Roo.MessageBox} This message box
30166          */
30167         confirm : function(title, msg, fn, scope){
30168             this.show({
30169                 title : title,
30170                 msg : msg,
30171                 buttons: this.YESNO,
30172                 fn: fn,
30173                 scope : scope,
30174                 modal : true
30175             });
30176             return this;
30177         },
30178
30179         /**
30180          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30181          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30182          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30183          * (could also be the top-right close button) and the text that was entered will be passed as the two
30184          * parameters to the callback.
30185          * @param {String} title The title bar text
30186          * @param {String} msg The message box body text
30187          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30188          * @param {Object} scope (optional) The scope of the callback function
30189          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30190          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30191          * @return {Roo.MessageBox} This message box
30192          */
30193         prompt : function(title, msg, fn, scope, multiline){
30194             this.show({
30195                 title : title,
30196                 msg : msg,
30197                 buttons: this.OKCANCEL,
30198                 fn: fn,
30199                 minWidth:250,
30200                 scope : scope,
30201                 prompt:true,
30202                 multiline: multiline,
30203                 modal : true
30204             });
30205             return this;
30206         },
30207
30208         /**
30209          * Button config that displays a single OK button
30210          * @type Object
30211          */
30212         OK : {ok:true},
30213         /**
30214          * Button config that displays Yes and No buttons
30215          * @type Object
30216          */
30217         YESNO : {yes:true, no:true},
30218         /**
30219          * Button config that displays OK and Cancel buttons
30220          * @type Object
30221          */
30222         OKCANCEL : {ok:true, cancel:true},
30223         /**
30224          * Button config that displays Yes, No and Cancel buttons
30225          * @type Object
30226          */
30227         YESNOCANCEL : {yes:true, no:true, cancel:true},
30228
30229         /**
30230          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30231          * @type Number
30232          */
30233         defaultTextHeight : 75,
30234         /**
30235          * The maximum width in pixels of the message box (defaults to 600)
30236          * @type Number
30237          */
30238         maxWidth : 600,
30239         /**
30240          * The minimum width in pixels of the message box (defaults to 100)
30241          * @type Number
30242          */
30243         minWidth : 100,
30244         /**
30245          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30246          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30247          * @type Number
30248          */
30249         minProgressWidth : 250,
30250         /**
30251          * An object containing the default button text strings that can be overriden for localized language support.
30252          * Supported properties are: ok, cancel, yes and no.
30253          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30254          * @type Object
30255          */
30256         buttonText : {
30257             ok : "OK",
30258             cancel : "Cancel",
30259             yes : "Yes",
30260             no : "No"
30261         }
30262     };
30263 }();
30264
30265 /**
30266  * Shorthand for {@link Roo.MessageBox}
30267  */
30268 Roo.Msg = Roo.MessageBox;/*
30269  * Based on:
30270  * Ext JS Library 1.1.1
30271  * Copyright(c) 2006-2007, Ext JS, LLC.
30272  *
30273  * Originally Released Under LGPL - original licence link has changed is not relivant.
30274  *
30275  * Fork - LGPL
30276  * <script type="text/javascript">
30277  */
30278 /**
30279  * @class Roo.QuickTips
30280  * Provides attractive and customizable tooltips for any element.
30281  * @singleton
30282  */
30283 Roo.QuickTips = function(){
30284     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30285     var ce, bd, xy, dd;
30286     var visible = false, disabled = true, inited = false;
30287     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30288     
30289     var onOver = function(e){
30290         if(disabled){
30291             return;
30292         }
30293         var t = e.getTarget();
30294         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30295             return;
30296         }
30297         if(ce && t == ce.el){
30298             clearTimeout(hideProc);
30299             return;
30300         }
30301         if(t && tagEls[t.id]){
30302             tagEls[t.id].el = t;
30303             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30304             return;
30305         }
30306         var ttp, et = Roo.fly(t);
30307         var ns = cfg.namespace;
30308         if(tm.interceptTitles && t.title){
30309             ttp = t.title;
30310             t.qtip = ttp;
30311             t.removeAttribute("title");
30312             e.preventDefault();
30313         }else{
30314             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30315         }
30316         if(ttp){
30317             showProc = show.defer(tm.showDelay, tm, [{
30318                 el: t, 
30319                 text: ttp, 
30320                 width: et.getAttributeNS(ns, cfg.width),
30321                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30322                 title: et.getAttributeNS(ns, cfg.title),
30323                     cls: et.getAttributeNS(ns, cfg.cls)
30324             }]);
30325         }
30326     };
30327     
30328     var onOut = function(e){
30329         clearTimeout(showProc);
30330         var t = e.getTarget();
30331         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30332             hideProc = setTimeout(hide, tm.hideDelay);
30333         }
30334     };
30335     
30336     var onMove = function(e){
30337         if(disabled){
30338             return;
30339         }
30340         xy = e.getXY();
30341         xy[1] += 18;
30342         if(tm.trackMouse && ce){
30343             el.setXY(xy);
30344         }
30345     };
30346     
30347     var onDown = function(e){
30348         clearTimeout(showProc);
30349         clearTimeout(hideProc);
30350         if(!e.within(el)){
30351             if(tm.hideOnClick){
30352                 hide();
30353                 tm.disable();
30354                 tm.enable.defer(100, tm);
30355             }
30356         }
30357     };
30358     
30359     var getPad = function(){
30360         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30361     };
30362
30363     var show = function(o){
30364         if(disabled){
30365             return;
30366         }
30367         clearTimeout(dismissProc);
30368         ce = o;
30369         if(removeCls){ // in case manually hidden
30370             el.removeClass(removeCls);
30371             removeCls = null;
30372         }
30373         if(ce.cls){
30374             el.addClass(ce.cls);
30375             removeCls = ce.cls;
30376         }
30377         if(ce.title){
30378             tipTitle.update(ce.title);
30379             tipTitle.show();
30380         }else{
30381             tipTitle.update('');
30382             tipTitle.hide();
30383         }
30384         el.dom.style.width  = tm.maxWidth+'px';
30385         //tipBody.dom.style.width = '';
30386         tipBodyText.update(o.text);
30387         var p = getPad(), w = ce.width;
30388         if(!w){
30389             var td = tipBodyText.dom;
30390             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30391             if(aw > tm.maxWidth){
30392                 w = tm.maxWidth;
30393             }else if(aw < tm.minWidth){
30394                 w = tm.minWidth;
30395             }else{
30396                 w = aw;
30397             }
30398         }
30399         //tipBody.setWidth(w);
30400         el.setWidth(parseInt(w, 10) + p);
30401         if(ce.autoHide === false){
30402             close.setDisplayed(true);
30403             if(dd){
30404                 dd.unlock();
30405             }
30406         }else{
30407             close.setDisplayed(false);
30408             if(dd){
30409                 dd.lock();
30410             }
30411         }
30412         if(xy){
30413             el.avoidY = xy[1]-18;
30414             el.setXY(xy);
30415         }
30416         if(tm.animate){
30417             el.setOpacity(.1);
30418             el.setStyle("visibility", "visible");
30419             el.fadeIn({callback: afterShow});
30420         }else{
30421             afterShow();
30422         }
30423     };
30424     
30425     var afterShow = function(){
30426         if(ce){
30427             el.show();
30428             esc.enable();
30429             if(tm.autoDismiss && ce.autoHide !== false){
30430                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30431             }
30432         }
30433     };
30434     
30435     var hide = function(noanim){
30436         clearTimeout(dismissProc);
30437         clearTimeout(hideProc);
30438         ce = null;
30439         if(el.isVisible()){
30440             esc.disable();
30441             if(noanim !== true && tm.animate){
30442                 el.fadeOut({callback: afterHide});
30443             }else{
30444                 afterHide();
30445             } 
30446         }
30447     };
30448     
30449     var afterHide = function(){
30450         el.hide();
30451         if(removeCls){
30452             el.removeClass(removeCls);
30453             removeCls = null;
30454         }
30455     };
30456     
30457     return {
30458         /**
30459         * @cfg {Number} minWidth
30460         * The minimum width of the quick tip (defaults to 40)
30461         */
30462        minWidth : 40,
30463         /**
30464         * @cfg {Number} maxWidth
30465         * The maximum width of the quick tip (defaults to 300)
30466         */
30467        maxWidth : 300,
30468         /**
30469         * @cfg {Boolean} interceptTitles
30470         * True to automatically use the element's DOM title value if available (defaults to false)
30471         */
30472        interceptTitles : false,
30473         /**
30474         * @cfg {Boolean} trackMouse
30475         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30476         */
30477        trackMouse : false,
30478         /**
30479         * @cfg {Boolean} hideOnClick
30480         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30481         */
30482        hideOnClick : true,
30483         /**
30484         * @cfg {Number} showDelay
30485         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30486         */
30487        showDelay : 500,
30488         /**
30489         * @cfg {Number} hideDelay
30490         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30491         */
30492        hideDelay : 200,
30493         /**
30494         * @cfg {Boolean} autoHide
30495         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30496         * Used in conjunction with hideDelay.
30497         */
30498        autoHide : true,
30499         /**
30500         * @cfg {Boolean}
30501         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30502         * (defaults to true).  Used in conjunction with autoDismissDelay.
30503         */
30504        autoDismiss : true,
30505         /**
30506         * @cfg {Number}
30507         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30508         */
30509        autoDismissDelay : 5000,
30510        /**
30511         * @cfg {Boolean} animate
30512         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30513         */
30514        animate : false,
30515
30516        /**
30517         * @cfg {String} title
30518         * Title text to display (defaults to '').  This can be any valid HTML markup.
30519         */
30520         title: '',
30521        /**
30522         * @cfg {String} text
30523         * Body text to display (defaults to '').  This can be any valid HTML markup.
30524         */
30525         text : '',
30526        /**
30527         * @cfg {String} cls
30528         * A CSS class to apply to the base quick tip element (defaults to '').
30529         */
30530         cls : '',
30531        /**
30532         * @cfg {Number} width
30533         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30534         * minWidth or maxWidth.
30535         */
30536         width : null,
30537
30538     /**
30539      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30540      * or display QuickTips in a page.
30541      */
30542        init : function(){
30543           tm = Roo.QuickTips;
30544           cfg = tm.tagConfig;
30545           if(!inited){
30546               if(!Roo.isReady){ // allow calling of init() before onReady
30547                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30548                   return;
30549               }
30550               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30551               el.fxDefaults = {stopFx: true};
30552               // maximum custom styling
30553               //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>');
30554               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>');              
30555               tipTitle = el.child('h3');
30556               tipTitle.enableDisplayMode("block");
30557               tipBody = el.child('div.x-tip-bd');
30558               tipBodyText = el.child('div.x-tip-bd-inner');
30559               //bdLeft = el.child('div.x-tip-bd-left');
30560               //bdRight = el.child('div.x-tip-bd-right');
30561               close = el.child('div.x-tip-close');
30562               close.enableDisplayMode("block");
30563               close.on("click", hide);
30564               var d = Roo.get(document);
30565               d.on("mousedown", onDown);
30566               d.on("mouseover", onOver);
30567               d.on("mouseout", onOut);
30568               d.on("mousemove", onMove);
30569               esc = d.addKeyListener(27, hide);
30570               esc.disable();
30571               if(Roo.dd.DD){
30572                   dd = el.initDD("default", null, {
30573                       onDrag : function(){
30574                           el.sync();  
30575                       }
30576                   });
30577                   dd.setHandleElId(tipTitle.id);
30578                   dd.lock();
30579               }
30580               inited = true;
30581           }
30582           this.enable(); 
30583        },
30584
30585     /**
30586      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30587      * are supported:
30588      * <pre>
30589 Property    Type                   Description
30590 ----------  ---------------------  ------------------------------------------------------------------------
30591 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30592      * </ul>
30593      * @param {Object} config The config object
30594      */
30595        register : function(config){
30596            var cs = config instanceof Array ? config : arguments;
30597            for(var i = 0, len = cs.length; i < len; i++) {
30598                var c = cs[i];
30599                var target = c.target;
30600                if(target){
30601                    if(target instanceof Array){
30602                        for(var j = 0, jlen = target.length; j < jlen; j++){
30603                            tagEls[target[j]] = c;
30604                        }
30605                    }else{
30606                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30607                    }
30608                }
30609            }
30610        },
30611
30612     /**
30613      * Removes this quick tip from its element and destroys it.
30614      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30615      */
30616        unregister : function(el){
30617            delete tagEls[Roo.id(el)];
30618        },
30619
30620     /**
30621      * Enable this quick tip.
30622      */
30623        enable : function(){
30624            if(inited && disabled){
30625                locks.pop();
30626                if(locks.length < 1){
30627                    disabled = false;
30628                }
30629            }
30630        },
30631
30632     /**
30633      * Disable this quick tip.
30634      */
30635        disable : function(){
30636           disabled = true;
30637           clearTimeout(showProc);
30638           clearTimeout(hideProc);
30639           clearTimeout(dismissProc);
30640           if(ce){
30641               hide(true);
30642           }
30643           locks.push(1);
30644        },
30645
30646     /**
30647      * Returns true if the quick tip is enabled, else false.
30648      */
30649        isEnabled : function(){
30650             return !disabled;
30651        },
30652
30653         // private
30654        tagConfig : {
30655            namespace : "ext",
30656            attribute : "qtip",
30657            width : "width",
30658            target : "target",
30659            title : "qtitle",
30660            hide : "hide",
30661            cls : "qclass"
30662        }
30663    };
30664 }();
30665
30666 // backwards compat
30667 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30668  * Based on:
30669  * Ext JS Library 1.1.1
30670  * Copyright(c) 2006-2007, Ext JS, LLC.
30671  *
30672  * Originally Released Under LGPL - original licence link has changed is not relivant.
30673  *
30674  * Fork - LGPL
30675  * <script type="text/javascript">
30676  */
30677  
30678
30679 /**
30680  * @class Roo.tree.TreePanel
30681  * @extends Roo.data.Tree
30682
30683  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30684  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30685  * @cfg {Boolean} enableDD true to enable drag and drop
30686  * @cfg {Boolean} enableDrag true to enable just drag
30687  * @cfg {Boolean} enableDrop true to enable just drop
30688  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30689  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30690  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30691  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30692  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30693  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30694  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30695  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30696  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30697  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30698  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30699  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30700  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30701  * @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>
30702  * @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>
30703  * 
30704  * @constructor
30705  * @param {String/HTMLElement/Element} el The container element
30706  * @param {Object} config
30707  */
30708 Roo.tree.TreePanel = function(el, config){
30709     var root = false;
30710     var loader = false;
30711     if (config.root) {
30712         root = config.root;
30713         delete config.root;
30714     }
30715     if (config.loader) {
30716         loader = config.loader;
30717         delete config.loader;
30718     }
30719     
30720     Roo.apply(this, config);
30721     Roo.tree.TreePanel.superclass.constructor.call(this);
30722     this.el = Roo.get(el);
30723     this.el.addClass('x-tree');
30724     //console.log(root);
30725     if (root) {
30726         this.setRootNode( Roo.factory(root, Roo.tree));
30727     }
30728     if (loader) {
30729         this.loader = Roo.factory(loader, Roo.tree);
30730     }
30731    /**
30732     * Read-only. The id of the container element becomes this TreePanel's id.
30733     */
30734    this.id = this.el.id;
30735    this.addEvents({
30736         /**
30737         * @event beforeload
30738         * Fires before a node is loaded, return false to cancel
30739         * @param {Node} node The node being loaded
30740         */
30741         "beforeload" : true,
30742         /**
30743         * @event load
30744         * Fires when a node is loaded
30745         * @param {Node} node The node that was loaded
30746         */
30747         "load" : true,
30748         /**
30749         * @event textchange
30750         * Fires when the text for a node is changed
30751         * @param {Node} node The node
30752         * @param {String} text The new text
30753         * @param {String} oldText The old text
30754         */
30755         "textchange" : true,
30756         /**
30757         * @event beforeexpand
30758         * Fires before a node is expanded, return false to cancel.
30759         * @param {Node} node The node
30760         * @param {Boolean} deep
30761         * @param {Boolean} anim
30762         */
30763         "beforeexpand" : true,
30764         /**
30765         * @event beforecollapse
30766         * Fires before a node is collapsed, return false to cancel.
30767         * @param {Node} node The node
30768         * @param {Boolean} deep
30769         * @param {Boolean} anim
30770         */
30771         "beforecollapse" : true,
30772         /**
30773         * @event expand
30774         * Fires when a node is expanded
30775         * @param {Node} node The node
30776         */
30777         "expand" : true,
30778         /**
30779         * @event disabledchange
30780         * Fires when the disabled status of a node changes
30781         * @param {Node} node The node
30782         * @param {Boolean} disabled
30783         */
30784         "disabledchange" : true,
30785         /**
30786         * @event collapse
30787         * Fires when a node is collapsed
30788         * @param {Node} node The node
30789         */
30790         "collapse" : true,
30791         /**
30792         * @event beforeclick
30793         * Fires before click processing on a node. Return false to cancel the default action.
30794         * @param {Node} node The node
30795         * @param {Roo.EventObject} e The event object
30796         */
30797         "beforeclick":true,
30798         /**
30799         * @event checkchange
30800         * Fires when a node with a checkbox's checked property changes
30801         * @param {Node} this This node
30802         * @param {Boolean} checked
30803         */
30804         "checkchange":true,
30805         /**
30806         * @event click
30807         * Fires when a node is clicked
30808         * @param {Node} node The node
30809         * @param {Roo.EventObject} e The event object
30810         */
30811         "click":true,
30812         /**
30813         * @event dblclick
30814         * Fires when a node is double clicked
30815         * @param {Node} node The node
30816         * @param {Roo.EventObject} e The event object
30817         */
30818         "dblclick":true,
30819         /**
30820         * @event contextmenu
30821         * Fires when a node is right clicked
30822         * @param {Node} node The node
30823         * @param {Roo.EventObject} e The event object
30824         */
30825         "contextmenu":true,
30826         /**
30827         * @event beforechildrenrendered
30828         * Fires right before the child nodes for a node are rendered
30829         * @param {Node} node The node
30830         */
30831         "beforechildrenrendered":true,
30832        /**
30833              * @event startdrag
30834              * Fires when a node starts being dragged
30835              * @param {Roo.tree.TreePanel} this
30836              * @param {Roo.tree.TreeNode} node
30837              * @param {event} e The raw browser event
30838              */ 
30839             "startdrag" : true,
30840             /**
30841              * @event enddrag
30842              * Fires when a drag operation is complete
30843              * @param {Roo.tree.TreePanel} this
30844              * @param {Roo.tree.TreeNode} node
30845              * @param {event} e The raw browser event
30846              */
30847             "enddrag" : true,
30848             /**
30849              * @event dragdrop
30850              * Fires when a dragged node is dropped on a valid DD target
30851              * @param {Roo.tree.TreePanel} this
30852              * @param {Roo.tree.TreeNode} node
30853              * @param {DD} dd The dd it was dropped on
30854              * @param {event} e The raw browser event
30855              */
30856             "dragdrop" : true,
30857             /**
30858              * @event beforenodedrop
30859              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30860              * passed to handlers has the following properties:<br />
30861              * <ul style="padding:5px;padding-left:16px;">
30862              * <li>tree - The TreePanel</li>
30863              * <li>target - The node being targeted for the drop</li>
30864              * <li>data - The drag data from the drag source</li>
30865              * <li>point - The point of the drop - append, above or below</li>
30866              * <li>source - The drag source</li>
30867              * <li>rawEvent - Raw mouse event</li>
30868              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30869              * to be inserted by setting them on this object.</li>
30870              * <li>cancel - Set this to true to cancel the drop.</li>
30871              * </ul>
30872              * @param {Object} dropEvent
30873              */
30874             "beforenodedrop" : true,
30875             /**
30876              * @event nodedrop
30877              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30878              * passed to handlers has the following properties:<br />
30879              * <ul style="padding:5px;padding-left:16px;">
30880              * <li>tree - The TreePanel</li>
30881              * <li>target - The node being targeted for the drop</li>
30882              * <li>data - The drag data from the drag source</li>
30883              * <li>point - The point of the drop - append, above or below</li>
30884              * <li>source - The drag source</li>
30885              * <li>rawEvent - Raw mouse event</li>
30886              * <li>dropNode - Dropped node(s).</li>
30887              * </ul>
30888              * @param {Object} dropEvent
30889              */
30890             "nodedrop" : true,
30891              /**
30892              * @event nodedragover
30893              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30894              * passed to handlers has the following properties:<br />
30895              * <ul style="padding:5px;padding-left:16px;">
30896              * <li>tree - The TreePanel</li>
30897              * <li>target - The node being targeted for the drop</li>
30898              * <li>data - The drag data from the drag source</li>
30899              * <li>point - The point of the drop - append, above or below</li>
30900              * <li>source - The drag source</li>
30901              * <li>rawEvent - Raw mouse event</li>
30902              * <li>dropNode - Drop node(s) provided by the source.</li>
30903              * <li>cancel - Set this to true to signal drop not allowed.</li>
30904              * </ul>
30905              * @param {Object} dragOverEvent
30906              */
30907             "nodedragover" : true
30908         
30909    });
30910    if(this.singleExpand){
30911        this.on("beforeexpand", this.restrictExpand, this);
30912    }
30913 };
30914 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30915     rootVisible : true,
30916     animate: Roo.enableFx,
30917     lines : true,
30918     enableDD : false,
30919     hlDrop : Roo.enableFx,
30920   
30921     renderer: false,
30922     
30923     rendererTip: false,
30924     // private
30925     restrictExpand : function(node){
30926         var p = node.parentNode;
30927         if(p){
30928             if(p.expandedChild && p.expandedChild.parentNode == p){
30929                 p.expandedChild.collapse();
30930             }
30931             p.expandedChild = node;
30932         }
30933     },
30934
30935     // private override
30936     setRootNode : function(node){
30937         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30938         if(!this.rootVisible){
30939             node.ui = new Roo.tree.RootTreeNodeUI(node);
30940         }
30941         return node;
30942     },
30943
30944     /**
30945      * Returns the container element for this TreePanel
30946      */
30947     getEl : function(){
30948         return this.el;
30949     },
30950
30951     /**
30952      * Returns the default TreeLoader for this TreePanel
30953      */
30954     getLoader : function(){
30955         return this.loader;
30956     },
30957
30958     /**
30959      * Expand all nodes
30960      */
30961     expandAll : function(){
30962         this.root.expand(true);
30963     },
30964
30965     /**
30966      * Collapse all nodes
30967      */
30968     collapseAll : function(){
30969         this.root.collapse(true);
30970     },
30971
30972     /**
30973      * Returns the selection model used by this TreePanel
30974      */
30975     getSelectionModel : function(){
30976         if(!this.selModel){
30977             this.selModel = new Roo.tree.DefaultSelectionModel();
30978         }
30979         return this.selModel;
30980     },
30981
30982     /**
30983      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30984      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30985      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30986      * @return {Array}
30987      */
30988     getChecked : function(a, startNode){
30989         startNode = startNode || this.root;
30990         var r = [];
30991         var f = function(){
30992             if(this.attributes.checked){
30993                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30994             }
30995         }
30996         startNode.cascade(f);
30997         return r;
30998     },
30999
31000     /**
31001      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31002      * @param {String} path
31003      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31004      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31005      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31006      */
31007     expandPath : function(path, attr, callback){
31008         attr = attr || "id";
31009         var keys = path.split(this.pathSeparator);
31010         var curNode = this.root;
31011         if(curNode.attributes[attr] != keys[1]){ // invalid root
31012             if(callback){
31013                 callback(false, null);
31014             }
31015             return;
31016         }
31017         var index = 1;
31018         var f = function(){
31019             if(++index == keys.length){
31020                 if(callback){
31021                     callback(true, curNode);
31022                 }
31023                 return;
31024             }
31025             var c = curNode.findChild(attr, keys[index]);
31026             if(!c){
31027                 if(callback){
31028                     callback(false, curNode);
31029                 }
31030                 return;
31031             }
31032             curNode = c;
31033             c.expand(false, false, f);
31034         };
31035         curNode.expand(false, false, f);
31036     },
31037
31038     /**
31039      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31040      * @param {String} path
31041      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31042      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31043      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31044      */
31045     selectPath : function(path, attr, callback){
31046         attr = attr || "id";
31047         var keys = path.split(this.pathSeparator);
31048         var v = keys.pop();
31049         if(keys.length > 0){
31050             var f = function(success, node){
31051                 if(success && node){
31052                     var n = node.findChild(attr, v);
31053                     if(n){
31054                         n.select();
31055                         if(callback){
31056                             callback(true, n);
31057                         }
31058                     }else if(callback){
31059                         callback(false, n);
31060                     }
31061                 }else{
31062                     if(callback){
31063                         callback(false, n);
31064                     }
31065                 }
31066             };
31067             this.expandPath(keys.join(this.pathSeparator), attr, f);
31068         }else{
31069             this.root.select();
31070             if(callback){
31071                 callback(true, this.root);
31072             }
31073         }
31074     },
31075
31076     getTreeEl : function(){
31077         return this.el;
31078     },
31079
31080     /**
31081      * Trigger rendering of this TreePanel
31082      */
31083     render : function(){
31084         if (this.innerCt) {
31085             return this; // stop it rendering more than once!!
31086         }
31087         
31088         this.innerCt = this.el.createChild({tag:"ul",
31089                cls:"x-tree-root-ct " +
31090                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31091
31092         if(this.containerScroll){
31093             Roo.dd.ScrollManager.register(this.el);
31094         }
31095         if((this.enableDD || this.enableDrop) && !this.dropZone){
31096            /**
31097             * The dropZone used by this tree if drop is enabled
31098             * @type Roo.tree.TreeDropZone
31099             */
31100              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31101                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31102            });
31103         }
31104         if((this.enableDD || this.enableDrag) && !this.dragZone){
31105            /**
31106             * The dragZone used by this tree if drag is enabled
31107             * @type Roo.tree.TreeDragZone
31108             */
31109             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31110                ddGroup: this.ddGroup || "TreeDD",
31111                scroll: this.ddScroll
31112            });
31113         }
31114         this.getSelectionModel().init(this);
31115         if (!this.root) {
31116             console.log("ROOT not set in tree");
31117             return;
31118         }
31119         this.root.render();
31120         if(!this.rootVisible){
31121             this.root.renderChildren();
31122         }
31123         return this;
31124     }
31125 });/*
31126  * Based on:
31127  * Ext JS Library 1.1.1
31128  * Copyright(c) 2006-2007, Ext JS, LLC.
31129  *
31130  * Originally Released Under LGPL - original licence link has changed is not relivant.
31131  *
31132  * Fork - LGPL
31133  * <script type="text/javascript">
31134  */
31135  
31136
31137 /**
31138  * @class Roo.tree.DefaultSelectionModel
31139  * @extends Roo.util.Observable
31140  * The default single selection for a TreePanel.
31141  */
31142 Roo.tree.DefaultSelectionModel = function(){
31143    this.selNode = null;
31144    
31145    this.addEvents({
31146        /**
31147         * @event selectionchange
31148         * Fires when the selected node changes
31149         * @param {DefaultSelectionModel} this
31150         * @param {TreeNode} node the new selection
31151         */
31152        "selectionchange" : true,
31153
31154        /**
31155         * @event beforeselect
31156         * Fires before the selected node changes, return false to cancel the change
31157         * @param {DefaultSelectionModel} this
31158         * @param {TreeNode} node the new selection
31159         * @param {TreeNode} node the old selection
31160         */
31161        "beforeselect" : true
31162    });
31163 };
31164
31165 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31166     init : function(tree){
31167         this.tree = tree;
31168         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31169         tree.on("click", this.onNodeClick, this);
31170     },
31171     
31172     onNodeClick : function(node, e){
31173         if (e.ctrlKey && this.selNode == node)  {
31174             this.unselect(node);
31175             return;
31176         }
31177         this.select(node);
31178     },
31179     
31180     /**
31181      * Select a node.
31182      * @param {TreeNode} node The node to select
31183      * @return {TreeNode} The selected node
31184      */
31185     select : function(node){
31186         var last = this.selNode;
31187         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31188             if(last){
31189                 last.ui.onSelectedChange(false);
31190             }
31191             this.selNode = node;
31192             node.ui.onSelectedChange(true);
31193             this.fireEvent("selectionchange", this, node, last);
31194         }
31195         return node;
31196     },
31197     
31198     /**
31199      * Deselect a node.
31200      * @param {TreeNode} node The node to unselect
31201      */
31202     unselect : function(node){
31203         if(this.selNode == node){
31204             this.clearSelections();
31205         }    
31206     },
31207     
31208     /**
31209      * Clear all selections
31210      */
31211     clearSelections : function(){
31212         var n = this.selNode;
31213         if(n){
31214             n.ui.onSelectedChange(false);
31215             this.selNode = null;
31216             this.fireEvent("selectionchange", this, null);
31217         }
31218         return n;
31219     },
31220     
31221     /**
31222      * Get the selected node
31223      * @return {TreeNode} The selected node
31224      */
31225     getSelectedNode : function(){
31226         return this.selNode;    
31227     },
31228     
31229     /**
31230      * Returns true if the node is selected
31231      * @param {TreeNode} node The node to check
31232      * @return {Boolean}
31233      */
31234     isSelected : function(node){
31235         return this.selNode == node;  
31236     },
31237
31238     /**
31239      * Selects the node above the selected node in the tree, intelligently walking the nodes
31240      * @return TreeNode The new selection
31241      */
31242     selectPrevious : function(){
31243         var s = this.selNode || this.lastSelNode;
31244         if(!s){
31245             return null;
31246         }
31247         var ps = s.previousSibling;
31248         if(ps){
31249             if(!ps.isExpanded() || ps.childNodes.length < 1){
31250                 return this.select(ps);
31251             } else{
31252                 var lc = ps.lastChild;
31253                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31254                     lc = lc.lastChild;
31255                 }
31256                 return this.select(lc);
31257             }
31258         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31259             return this.select(s.parentNode);
31260         }
31261         return null;
31262     },
31263
31264     /**
31265      * Selects the node above the selected node in the tree, intelligently walking the nodes
31266      * @return TreeNode The new selection
31267      */
31268     selectNext : function(){
31269         var s = this.selNode || this.lastSelNode;
31270         if(!s){
31271             return null;
31272         }
31273         if(s.firstChild && s.isExpanded()){
31274              return this.select(s.firstChild);
31275          }else if(s.nextSibling){
31276              return this.select(s.nextSibling);
31277          }else if(s.parentNode){
31278             var newS = null;
31279             s.parentNode.bubble(function(){
31280                 if(this.nextSibling){
31281                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31282                     return false;
31283                 }
31284             });
31285             return newS;
31286          }
31287         return null;
31288     },
31289
31290     onKeyDown : function(e){
31291         var s = this.selNode || this.lastSelNode;
31292         // undesirable, but required
31293         var sm = this;
31294         if(!s){
31295             return;
31296         }
31297         var k = e.getKey();
31298         switch(k){
31299              case e.DOWN:
31300                  e.stopEvent();
31301                  this.selectNext();
31302              break;
31303              case e.UP:
31304                  e.stopEvent();
31305                  this.selectPrevious();
31306              break;
31307              case e.RIGHT:
31308                  e.preventDefault();
31309                  if(s.hasChildNodes()){
31310                      if(!s.isExpanded()){
31311                          s.expand();
31312                      }else if(s.firstChild){
31313                          this.select(s.firstChild, e);
31314                      }
31315                  }
31316              break;
31317              case e.LEFT:
31318                  e.preventDefault();
31319                  if(s.hasChildNodes() && s.isExpanded()){
31320                      s.collapse();
31321                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31322                      this.select(s.parentNode, e);
31323                  }
31324              break;
31325         };
31326     }
31327 });
31328
31329 /**
31330  * @class Roo.tree.MultiSelectionModel
31331  * @extends Roo.util.Observable
31332  * Multi selection for a TreePanel.
31333  */
31334 Roo.tree.MultiSelectionModel = function(){
31335    this.selNodes = [];
31336    this.selMap = {};
31337    this.addEvents({
31338        /**
31339         * @event selectionchange
31340         * Fires when the selected nodes change
31341         * @param {MultiSelectionModel} this
31342         * @param {Array} nodes Array of the selected nodes
31343         */
31344        "selectionchange" : true
31345    });
31346 };
31347
31348 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31349     init : function(tree){
31350         this.tree = tree;
31351         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31352         tree.on("click", this.onNodeClick, this);
31353     },
31354     
31355     onNodeClick : function(node, e){
31356         this.select(node, e, e.ctrlKey);
31357     },
31358     
31359     /**
31360      * Select a node.
31361      * @param {TreeNode} node The node to select
31362      * @param {EventObject} e (optional) An event associated with the selection
31363      * @param {Boolean} keepExisting True to retain existing selections
31364      * @return {TreeNode} The selected node
31365      */
31366     select : function(node, e, keepExisting){
31367         if(keepExisting !== true){
31368             this.clearSelections(true);
31369         }
31370         if(this.isSelected(node)){
31371             this.lastSelNode = node;
31372             return node;
31373         }
31374         this.selNodes.push(node);
31375         this.selMap[node.id] = node;
31376         this.lastSelNode = node;
31377         node.ui.onSelectedChange(true);
31378         this.fireEvent("selectionchange", this, this.selNodes);
31379         return node;
31380     },
31381     
31382     /**
31383      * Deselect a node.
31384      * @param {TreeNode} node The node to unselect
31385      */
31386     unselect : function(node){
31387         if(this.selMap[node.id]){
31388             node.ui.onSelectedChange(false);
31389             var sn = this.selNodes;
31390             var index = -1;
31391             if(sn.indexOf){
31392                 index = sn.indexOf(node);
31393             }else{
31394                 for(var i = 0, len = sn.length; i < len; i++){
31395                     if(sn[i] == node){
31396                         index = i;
31397                         break;
31398                     }
31399                 }
31400             }
31401             if(index != -1){
31402                 this.selNodes.splice(index, 1);
31403             }
31404             delete this.selMap[node.id];
31405             this.fireEvent("selectionchange", this, this.selNodes);
31406         }
31407     },
31408     
31409     /**
31410      * Clear all selections
31411      */
31412     clearSelections : function(suppressEvent){
31413         var sn = this.selNodes;
31414         if(sn.length > 0){
31415             for(var i = 0, len = sn.length; i < len; i++){
31416                 sn[i].ui.onSelectedChange(false);
31417             }
31418             this.selNodes = [];
31419             this.selMap = {};
31420             if(suppressEvent !== true){
31421                 this.fireEvent("selectionchange", this, this.selNodes);
31422             }
31423         }
31424     },
31425     
31426     /**
31427      * Returns true if the node is selected
31428      * @param {TreeNode} node The node to check
31429      * @return {Boolean}
31430      */
31431     isSelected : function(node){
31432         return this.selMap[node.id] ? true : false;  
31433     },
31434     
31435     /**
31436      * Returns an array of the selected nodes
31437      * @return {Array}
31438      */
31439     getSelectedNodes : function(){
31440         return this.selNodes;    
31441     },
31442
31443     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31444
31445     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31446
31447     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31448 });/*
31449  * Based on:
31450  * Ext JS Library 1.1.1
31451  * Copyright(c) 2006-2007, Ext JS, LLC.
31452  *
31453  * Originally Released Under LGPL - original licence link has changed is not relivant.
31454  *
31455  * Fork - LGPL
31456  * <script type="text/javascript">
31457  */
31458  
31459 /**
31460  * @class Roo.tree.TreeNode
31461  * @extends Roo.data.Node
31462  * @cfg {String} text The text for this node
31463  * @cfg {Boolean} expanded true to start the node expanded
31464  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31465  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31466  * @cfg {Boolean} disabled true to start the node disabled
31467  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31468  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31469  * @cfg {String} cls A css class to be added to the node
31470  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31471  * @cfg {String} href URL of the link used for the node (defaults to #)
31472  * @cfg {String} hrefTarget target frame for the link
31473  * @cfg {String} qtip An Ext QuickTip for the node
31474  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31475  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31476  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31477  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31478  * (defaults to undefined with no checkbox rendered)
31479  * @constructor
31480  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31481  */
31482 Roo.tree.TreeNode = function(attributes){
31483     attributes = attributes || {};
31484     if(typeof attributes == "string"){
31485         attributes = {text: attributes};
31486     }
31487     this.childrenRendered = false;
31488     this.rendered = false;
31489     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31490     this.expanded = attributes.expanded === true;
31491     this.isTarget = attributes.isTarget !== false;
31492     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31493     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31494
31495     /**
31496      * Read-only. The text for this node. To change it use setText().
31497      * @type String
31498      */
31499     this.text = attributes.text;
31500     /**
31501      * True if this node is disabled.
31502      * @type Boolean
31503      */
31504     this.disabled = attributes.disabled === true;
31505
31506     this.addEvents({
31507         /**
31508         * @event textchange
31509         * Fires when the text for this node is changed
31510         * @param {Node} this This node
31511         * @param {String} text The new text
31512         * @param {String} oldText The old text
31513         */
31514         "textchange" : true,
31515         /**
31516         * @event beforeexpand
31517         * Fires before this node is expanded, return false to cancel.
31518         * @param {Node} this This node
31519         * @param {Boolean} deep
31520         * @param {Boolean} anim
31521         */
31522         "beforeexpand" : true,
31523         /**
31524         * @event beforecollapse
31525         * Fires before this node is collapsed, return false to cancel.
31526         * @param {Node} this This node
31527         * @param {Boolean} deep
31528         * @param {Boolean} anim
31529         */
31530         "beforecollapse" : true,
31531         /**
31532         * @event expand
31533         * Fires when this node is expanded
31534         * @param {Node} this This node
31535         */
31536         "expand" : true,
31537         /**
31538         * @event disabledchange
31539         * Fires when the disabled status of this node changes
31540         * @param {Node} this This node
31541         * @param {Boolean} disabled
31542         */
31543         "disabledchange" : true,
31544         /**
31545         * @event collapse
31546         * Fires when this node is collapsed
31547         * @param {Node} this This node
31548         */
31549         "collapse" : true,
31550         /**
31551         * @event beforeclick
31552         * Fires before click processing. Return false to cancel the default action.
31553         * @param {Node} this This node
31554         * @param {Roo.EventObject} e The event object
31555         */
31556         "beforeclick":true,
31557         /**
31558         * @event checkchange
31559         * Fires when a node with a checkbox's checked property changes
31560         * @param {Node} this This node
31561         * @param {Boolean} checked
31562         */
31563         "checkchange":true,
31564         /**
31565         * @event click
31566         * Fires when this node is clicked
31567         * @param {Node} this This node
31568         * @param {Roo.EventObject} e The event object
31569         */
31570         "click":true,
31571         /**
31572         * @event dblclick
31573         * Fires when this node is double clicked
31574         * @param {Node} this This node
31575         * @param {Roo.EventObject} e The event object
31576         */
31577         "dblclick":true,
31578         /**
31579         * @event contextmenu
31580         * Fires when this node is right clicked
31581         * @param {Node} this This node
31582         * @param {Roo.EventObject} e The event object
31583         */
31584         "contextmenu":true,
31585         /**
31586         * @event beforechildrenrendered
31587         * Fires right before the child nodes for this node are rendered
31588         * @param {Node} this This node
31589         */
31590         "beforechildrenrendered":true
31591     });
31592
31593     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31594
31595     /**
31596      * Read-only. The UI for this node
31597      * @type TreeNodeUI
31598      */
31599     this.ui = new uiClass(this);
31600 };
31601 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31602     preventHScroll: true,
31603     /**
31604      * Returns true if this node is expanded
31605      * @return {Boolean}
31606      */
31607     isExpanded : function(){
31608         return this.expanded;
31609     },
31610
31611     /**
31612      * Returns the UI object for this node
31613      * @return {TreeNodeUI}
31614      */
31615     getUI : function(){
31616         return this.ui;
31617     },
31618
31619     // private override
31620     setFirstChild : function(node){
31621         var of = this.firstChild;
31622         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31623         if(this.childrenRendered && of && node != of){
31624             of.renderIndent(true, true);
31625         }
31626         if(this.rendered){
31627             this.renderIndent(true, true);
31628         }
31629     },
31630
31631     // private override
31632     setLastChild : function(node){
31633         var ol = this.lastChild;
31634         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31635         if(this.childrenRendered && ol && node != ol){
31636             ol.renderIndent(true, true);
31637         }
31638         if(this.rendered){
31639             this.renderIndent(true, true);
31640         }
31641     },
31642
31643     // these methods are overridden to provide lazy rendering support
31644     // private override
31645     appendChild : function(){
31646         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31647         if(node && this.childrenRendered){
31648             node.render();
31649         }
31650         this.ui.updateExpandIcon();
31651         return node;
31652     },
31653
31654     // private override
31655     removeChild : function(node){
31656         this.ownerTree.getSelectionModel().unselect(node);
31657         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31658         // if it's been rendered remove dom node
31659         if(this.childrenRendered){
31660             node.ui.remove();
31661         }
31662         if(this.childNodes.length < 1){
31663             this.collapse(false, false);
31664         }else{
31665             this.ui.updateExpandIcon();
31666         }
31667         if(!this.firstChild) {
31668             this.childrenRendered = false;
31669         }
31670         return node;
31671     },
31672
31673     // private override
31674     insertBefore : function(node, refNode){
31675         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31676         if(newNode && refNode && this.childrenRendered){
31677             node.render();
31678         }
31679         this.ui.updateExpandIcon();
31680         return newNode;
31681     },
31682
31683     /**
31684      * Sets the text for this node
31685      * @param {String} text
31686      */
31687     setText : function(text){
31688         var oldText = this.text;
31689         this.text = text;
31690         this.attributes.text = text;
31691         if(this.rendered){ // event without subscribing
31692             this.ui.onTextChange(this, text, oldText);
31693         }
31694         this.fireEvent("textchange", this, text, oldText);
31695     },
31696
31697     /**
31698      * Triggers selection of this node
31699      */
31700     select : function(){
31701         this.getOwnerTree().getSelectionModel().select(this);
31702     },
31703
31704     /**
31705      * Triggers deselection of this node
31706      */
31707     unselect : function(){
31708         this.getOwnerTree().getSelectionModel().unselect(this);
31709     },
31710
31711     /**
31712      * Returns true if this node is selected
31713      * @return {Boolean}
31714      */
31715     isSelected : function(){
31716         return this.getOwnerTree().getSelectionModel().isSelected(this);
31717     },
31718
31719     /**
31720      * Expand this node.
31721      * @param {Boolean} deep (optional) True to expand all children as well
31722      * @param {Boolean} anim (optional) false to cancel the default animation
31723      * @param {Function} callback (optional) A callback to be called when
31724      * expanding this node completes (does not wait for deep expand to complete).
31725      * Called with 1 parameter, this node.
31726      */
31727     expand : function(deep, anim, callback){
31728         if(!this.expanded){
31729             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31730                 return;
31731             }
31732             if(!this.childrenRendered){
31733                 this.renderChildren();
31734             }
31735             this.expanded = true;
31736             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31737                 this.ui.animExpand(function(){
31738                     this.fireEvent("expand", this);
31739                     if(typeof callback == "function"){
31740                         callback(this);
31741                     }
31742                     if(deep === true){
31743                         this.expandChildNodes(true);
31744                     }
31745                 }.createDelegate(this));
31746                 return;
31747             }else{
31748                 this.ui.expand();
31749                 this.fireEvent("expand", this);
31750                 if(typeof callback == "function"){
31751                     callback(this);
31752                 }
31753             }
31754         }else{
31755            if(typeof callback == "function"){
31756                callback(this);
31757            }
31758         }
31759         if(deep === true){
31760             this.expandChildNodes(true);
31761         }
31762     },
31763
31764     isHiddenRoot : function(){
31765         return this.isRoot && !this.getOwnerTree().rootVisible;
31766     },
31767
31768     /**
31769      * Collapse this node.
31770      * @param {Boolean} deep (optional) True to collapse all children as well
31771      * @param {Boolean} anim (optional) false to cancel the default animation
31772      */
31773     collapse : function(deep, anim){
31774         if(this.expanded && !this.isHiddenRoot()){
31775             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31776                 return;
31777             }
31778             this.expanded = false;
31779             if((this.getOwnerTree().animate && anim !== false) || anim){
31780                 this.ui.animCollapse(function(){
31781                     this.fireEvent("collapse", this);
31782                     if(deep === true){
31783                         this.collapseChildNodes(true);
31784                     }
31785                 }.createDelegate(this));
31786                 return;
31787             }else{
31788                 this.ui.collapse();
31789                 this.fireEvent("collapse", this);
31790             }
31791         }
31792         if(deep === true){
31793             var cs = this.childNodes;
31794             for(var i = 0, len = cs.length; i < len; i++) {
31795                 cs[i].collapse(true, false);
31796             }
31797         }
31798     },
31799
31800     // private
31801     delayedExpand : function(delay){
31802         if(!this.expandProcId){
31803             this.expandProcId = this.expand.defer(delay, this);
31804         }
31805     },
31806
31807     // private
31808     cancelExpand : function(){
31809         if(this.expandProcId){
31810             clearTimeout(this.expandProcId);
31811         }
31812         this.expandProcId = false;
31813     },
31814
31815     /**
31816      * Toggles expanded/collapsed state of the node
31817      */
31818     toggle : function(){
31819         if(this.expanded){
31820             this.collapse();
31821         }else{
31822             this.expand();
31823         }
31824     },
31825
31826     /**
31827      * Ensures all parent nodes are expanded
31828      */
31829     ensureVisible : function(callback){
31830         var tree = this.getOwnerTree();
31831         tree.expandPath(this.parentNode.getPath(), false, function(){
31832             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31833             Roo.callback(callback);
31834         }.createDelegate(this));
31835     },
31836
31837     /**
31838      * Expand all child nodes
31839      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31840      */
31841     expandChildNodes : function(deep){
31842         var cs = this.childNodes;
31843         for(var i = 0, len = cs.length; i < len; i++) {
31844                 cs[i].expand(deep);
31845         }
31846     },
31847
31848     /**
31849      * Collapse all child nodes
31850      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31851      */
31852     collapseChildNodes : function(deep){
31853         var cs = this.childNodes;
31854         for(var i = 0, len = cs.length; i < len; i++) {
31855                 cs[i].collapse(deep);
31856         }
31857     },
31858
31859     /**
31860      * Disables this node
31861      */
31862     disable : function(){
31863         this.disabled = true;
31864         this.unselect();
31865         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31866             this.ui.onDisableChange(this, true);
31867         }
31868         this.fireEvent("disabledchange", this, true);
31869     },
31870
31871     /**
31872      * Enables this node
31873      */
31874     enable : function(){
31875         this.disabled = false;
31876         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31877             this.ui.onDisableChange(this, false);
31878         }
31879         this.fireEvent("disabledchange", this, false);
31880     },
31881
31882     // private
31883     renderChildren : function(suppressEvent){
31884         if(suppressEvent !== false){
31885             this.fireEvent("beforechildrenrendered", this);
31886         }
31887         var cs = this.childNodes;
31888         for(var i = 0, len = cs.length; i < len; i++){
31889             cs[i].render(true);
31890         }
31891         this.childrenRendered = true;
31892     },
31893
31894     // private
31895     sort : function(fn, scope){
31896         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31897         if(this.childrenRendered){
31898             var cs = this.childNodes;
31899             for(var i = 0, len = cs.length; i < len; i++){
31900                 cs[i].render(true);
31901             }
31902         }
31903     },
31904
31905     // private
31906     render : function(bulkRender){
31907         this.ui.render(bulkRender);
31908         if(!this.rendered){
31909             this.rendered = true;
31910             if(this.expanded){
31911                 this.expanded = false;
31912                 this.expand(false, false);
31913             }
31914         }
31915     },
31916
31917     // private
31918     renderIndent : function(deep, refresh){
31919         if(refresh){
31920             this.ui.childIndent = null;
31921         }
31922         this.ui.renderIndent();
31923         if(deep === true && this.childrenRendered){
31924             var cs = this.childNodes;
31925             for(var i = 0, len = cs.length; i < len; i++){
31926                 cs[i].renderIndent(true, refresh);
31927             }
31928         }
31929     }
31930 });/*
31931  * Based on:
31932  * Ext JS Library 1.1.1
31933  * Copyright(c) 2006-2007, Ext JS, LLC.
31934  *
31935  * Originally Released Under LGPL - original licence link has changed is not relivant.
31936  *
31937  * Fork - LGPL
31938  * <script type="text/javascript">
31939  */
31940  
31941 /**
31942  * @class Roo.tree.AsyncTreeNode
31943  * @extends Roo.tree.TreeNode
31944  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31945  * @constructor
31946  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31947  */
31948  Roo.tree.AsyncTreeNode = function(config){
31949     this.loaded = false;
31950     this.loading = false;
31951     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31952     /**
31953     * @event beforeload
31954     * Fires before this node is loaded, return false to cancel
31955     * @param {Node} this This node
31956     */
31957     this.addEvents({'beforeload':true, 'load': true});
31958     /**
31959     * @event load
31960     * Fires when this node is loaded
31961     * @param {Node} this This node
31962     */
31963     /**
31964      * The loader used by this node (defaults to using the tree's defined loader)
31965      * @type TreeLoader
31966      * @property loader
31967      */
31968 };
31969 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31970     expand : function(deep, anim, callback){
31971         if(this.loading){ // if an async load is already running, waiting til it's done
31972             var timer;
31973             var f = function(){
31974                 if(!this.loading){ // done loading
31975                     clearInterval(timer);
31976                     this.expand(deep, anim, callback);
31977                 }
31978             }.createDelegate(this);
31979             timer = setInterval(f, 200);
31980             return;
31981         }
31982         if(!this.loaded){
31983             if(this.fireEvent("beforeload", this) === false){
31984                 return;
31985             }
31986             this.loading = true;
31987             this.ui.beforeLoad(this);
31988             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31989             if(loader){
31990                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31991                 return;
31992             }
31993         }
31994         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31995     },
31996     
31997     /**
31998      * Returns true if this node is currently loading
31999      * @return {Boolean}
32000      */
32001     isLoading : function(){
32002         return this.loading;  
32003     },
32004     
32005     loadComplete : function(deep, anim, callback){
32006         this.loading = false;
32007         this.loaded = true;
32008         this.ui.afterLoad(this);
32009         this.fireEvent("load", this);
32010         this.expand(deep, anim, callback);
32011     },
32012     
32013     /**
32014      * Returns true if this node has been loaded
32015      * @return {Boolean}
32016      */
32017     isLoaded : function(){
32018         return this.loaded;
32019     },
32020     
32021     hasChildNodes : function(){
32022         if(!this.isLeaf() && !this.loaded){
32023             return true;
32024         }else{
32025             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32026         }
32027     },
32028
32029     /**
32030      * Trigger a reload for this node
32031      * @param {Function} callback
32032      */
32033     reload : function(callback){
32034         this.collapse(false, false);
32035         while(this.firstChild){
32036             this.removeChild(this.firstChild);
32037         }
32038         this.childrenRendered = false;
32039         this.loaded = false;
32040         if(this.isHiddenRoot()){
32041             this.expanded = false;
32042         }
32043         this.expand(false, false, callback);
32044     }
32045 });/*
32046  * Based on:
32047  * Ext JS Library 1.1.1
32048  * Copyright(c) 2006-2007, Ext JS, LLC.
32049  *
32050  * Originally Released Under LGPL - original licence link has changed is not relivant.
32051  *
32052  * Fork - LGPL
32053  * <script type="text/javascript">
32054  */
32055  
32056 /**
32057  * @class Roo.tree.TreeNodeUI
32058  * @constructor
32059  * @param {Object} node The node to render
32060  * The TreeNode UI implementation is separate from the
32061  * tree implementation. Unless you are customizing the tree UI,
32062  * you should never have to use this directly.
32063  */
32064 Roo.tree.TreeNodeUI = function(node){
32065     this.node = node;
32066     this.rendered = false;
32067     this.animating = false;
32068     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32069 };
32070
32071 Roo.tree.TreeNodeUI.prototype = {
32072     removeChild : function(node){
32073         if(this.rendered){
32074             this.ctNode.removeChild(node.ui.getEl());
32075         }
32076     },
32077
32078     beforeLoad : function(){
32079          this.addClass("x-tree-node-loading");
32080     },
32081
32082     afterLoad : function(){
32083          this.removeClass("x-tree-node-loading");
32084     },
32085
32086     onTextChange : function(node, text, oldText){
32087         if(this.rendered){
32088             this.textNode.innerHTML = text;
32089         }
32090     },
32091
32092     onDisableChange : function(node, state){
32093         this.disabled = state;
32094         if(state){
32095             this.addClass("x-tree-node-disabled");
32096         }else{
32097             this.removeClass("x-tree-node-disabled");
32098         }
32099     },
32100
32101     onSelectedChange : function(state){
32102         if(state){
32103             this.focus();
32104             this.addClass("x-tree-selected");
32105         }else{
32106             //this.blur();
32107             this.removeClass("x-tree-selected");
32108         }
32109     },
32110
32111     onMove : function(tree, node, oldParent, newParent, index, refNode){
32112         this.childIndent = null;
32113         if(this.rendered){
32114             var targetNode = newParent.ui.getContainer();
32115             if(!targetNode){//target not rendered
32116                 this.holder = document.createElement("div");
32117                 this.holder.appendChild(this.wrap);
32118                 return;
32119             }
32120             var insertBefore = refNode ? refNode.ui.getEl() : null;
32121             if(insertBefore){
32122                 targetNode.insertBefore(this.wrap, insertBefore);
32123             }else{
32124                 targetNode.appendChild(this.wrap);
32125             }
32126             this.node.renderIndent(true);
32127         }
32128     },
32129
32130     addClass : function(cls){
32131         if(this.elNode){
32132             Roo.fly(this.elNode).addClass(cls);
32133         }
32134     },
32135
32136     removeClass : function(cls){
32137         if(this.elNode){
32138             Roo.fly(this.elNode).removeClass(cls);
32139         }
32140     },
32141
32142     remove : function(){
32143         if(this.rendered){
32144             this.holder = document.createElement("div");
32145             this.holder.appendChild(this.wrap);
32146         }
32147     },
32148
32149     fireEvent : function(){
32150         return this.node.fireEvent.apply(this.node, arguments);
32151     },
32152
32153     initEvents : function(){
32154         this.node.on("move", this.onMove, this);
32155         var E = Roo.EventManager;
32156         var a = this.anchor;
32157
32158         var el = Roo.fly(a, '_treeui');
32159
32160         if(Roo.isOpera){ // opera render bug ignores the CSS
32161             el.setStyle("text-decoration", "none");
32162         }
32163
32164         el.on("click", this.onClick, this);
32165         el.on("dblclick", this.onDblClick, this);
32166
32167         if(this.checkbox){
32168             Roo.EventManager.on(this.checkbox,
32169                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32170         }
32171
32172         el.on("contextmenu", this.onContextMenu, this);
32173
32174         var icon = Roo.fly(this.iconNode);
32175         icon.on("click", this.onClick, this);
32176         icon.on("dblclick", this.onDblClick, this);
32177         icon.on("contextmenu", this.onContextMenu, this);
32178         E.on(this.ecNode, "click", this.ecClick, this, true);
32179
32180         if(this.node.disabled){
32181             this.addClass("x-tree-node-disabled");
32182         }
32183         if(this.node.hidden){
32184             this.addClass("x-tree-node-disabled");
32185         }
32186         var ot = this.node.getOwnerTree();
32187         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32188         if(dd && (!this.node.isRoot || ot.rootVisible)){
32189             Roo.dd.Registry.register(this.elNode, {
32190                 node: this.node,
32191                 handles: this.getDDHandles(),
32192                 isHandle: false
32193             });
32194         }
32195     },
32196
32197     getDDHandles : function(){
32198         return [this.iconNode, this.textNode];
32199     },
32200
32201     hide : function(){
32202         if(this.rendered){
32203             this.wrap.style.display = "none";
32204         }
32205     },
32206
32207     show : function(){
32208         if(this.rendered){
32209             this.wrap.style.display = "";
32210         }
32211     },
32212
32213     onContextMenu : function(e){
32214         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32215             e.preventDefault();
32216             this.focus();
32217             this.fireEvent("contextmenu", this.node, e);
32218         }
32219     },
32220
32221     onClick : function(e){
32222         if(this.dropping){
32223             e.stopEvent();
32224             return;
32225         }
32226         if(this.fireEvent("beforeclick", this.node, e) !== false){
32227             if(!this.disabled && this.node.attributes.href){
32228                 this.fireEvent("click", this.node, e);
32229                 return;
32230             }
32231             e.preventDefault();
32232             if(this.disabled){
32233                 return;
32234             }
32235
32236             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32237                 this.node.toggle();
32238             }
32239
32240             this.fireEvent("click", this.node, e);
32241         }else{
32242             e.stopEvent();
32243         }
32244     },
32245
32246     onDblClick : function(e){
32247         e.preventDefault();
32248         if(this.disabled){
32249             return;
32250         }
32251         if(this.checkbox){
32252             this.toggleCheck();
32253         }
32254         if(!this.animating && this.node.hasChildNodes()){
32255             this.node.toggle();
32256         }
32257         this.fireEvent("dblclick", this.node, e);
32258     },
32259
32260     onCheckChange : function(){
32261         var checked = this.checkbox.checked;
32262         this.node.attributes.checked = checked;
32263         this.fireEvent('checkchange', this.node, checked);
32264     },
32265
32266     ecClick : function(e){
32267         if(!this.animating && this.node.hasChildNodes()){
32268             this.node.toggle();
32269         }
32270     },
32271
32272     startDrop : function(){
32273         this.dropping = true;
32274     },
32275
32276     // delayed drop so the click event doesn't get fired on a drop
32277     endDrop : function(){
32278        setTimeout(function(){
32279            this.dropping = false;
32280        }.createDelegate(this), 50);
32281     },
32282
32283     expand : function(){
32284         this.updateExpandIcon();
32285         this.ctNode.style.display = "";
32286     },
32287
32288     focus : function(){
32289         if(!this.node.preventHScroll){
32290             try{this.anchor.focus();
32291             }catch(e){}
32292         }else if(!Roo.isIE){
32293             try{
32294                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32295                 var l = noscroll.scrollLeft;
32296                 this.anchor.focus();
32297                 noscroll.scrollLeft = l;
32298             }catch(e){}
32299         }
32300     },
32301
32302     toggleCheck : function(value){
32303         var cb = this.checkbox;
32304         if(cb){
32305             cb.checked = (value === undefined ? !cb.checked : value);
32306         }
32307     },
32308
32309     blur : function(){
32310         try{
32311             this.anchor.blur();
32312         }catch(e){}
32313     },
32314
32315     animExpand : function(callback){
32316         var ct = Roo.get(this.ctNode);
32317         ct.stopFx();
32318         if(!this.node.hasChildNodes()){
32319             this.updateExpandIcon();
32320             this.ctNode.style.display = "";
32321             Roo.callback(callback);
32322             return;
32323         }
32324         this.animating = true;
32325         this.updateExpandIcon();
32326
32327         ct.slideIn('t', {
32328            callback : function(){
32329                this.animating = false;
32330                Roo.callback(callback);
32331             },
32332             scope: this,
32333             duration: this.node.ownerTree.duration || .25
32334         });
32335     },
32336
32337     highlight : function(){
32338         var tree = this.node.getOwnerTree();
32339         Roo.fly(this.wrap).highlight(
32340             tree.hlColor || "C3DAF9",
32341             {endColor: tree.hlBaseColor}
32342         );
32343     },
32344
32345     collapse : function(){
32346         this.updateExpandIcon();
32347         this.ctNode.style.display = "none";
32348     },
32349
32350     animCollapse : function(callback){
32351         var ct = Roo.get(this.ctNode);
32352         ct.enableDisplayMode('block');
32353         ct.stopFx();
32354
32355         this.animating = true;
32356         this.updateExpandIcon();
32357
32358         ct.slideOut('t', {
32359             callback : function(){
32360                this.animating = false;
32361                Roo.callback(callback);
32362             },
32363             scope: this,
32364             duration: this.node.ownerTree.duration || .25
32365         });
32366     },
32367
32368     getContainer : function(){
32369         return this.ctNode;
32370     },
32371
32372     getEl : function(){
32373         return this.wrap;
32374     },
32375
32376     appendDDGhost : function(ghostNode){
32377         ghostNode.appendChild(this.elNode.cloneNode(true));
32378     },
32379
32380     getDDRepairXY : function(){
32381         return Roo.lib.Dom.getXY(this.iconNode);
32382     },
32383
32384     onRender : function(){
32385         this.render();
32386     },
32387
32388     render : function(bulkRender){
32389         var n = this.node, a = n.attributes;
32390         var targetNode = n.parentNode ?
32391               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32392
32393         if(!this.rendered){
32394             this.rendered = true;
32395
32396             this.renderElements(n, a, targetNode, bulkRender);
32397
32398             if(a.qtip){
32399                if(this.textNode.setAttributeNS){
32400                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32401                    if(a.qtipTitle){
32402                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32403                    }
32404                }else{
32405                    this.textNode.setAttribute("ext:qtip", a.qtip);
32406                    if(a.qtipTitle){
32407                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32408                    }
32409                }
32410             }else if(a.qtipCfg){
32411                 a.qtipCfg.target = Roo.id(this.textNode);
32412                 Roo.QuickTips.register(a.qtipCfg);
32413             }
32414             this.initEvents();
32415             if(!this.node.expanded){
32416                 this.updateExpandIcon();
32417             }
32418         }else{
32419             if(bulkRender === true) {
32420                 targetNode.appendChild(this.wrap);
32421             }
32422         }
32423     },
32424
32425     renderElements : function(n, a, targetNode, bulkRender){
32426         // add some indent caching, this helps performance when rendering a large tree
32427         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32428         var t = n.getOwnerTree();
32429         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32430         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32431         var cb = typeof a.checked == 'boolean';
32432         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32433         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32434             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32435             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32436             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32437             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32438             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32439              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32440                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32441             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32442             "</li>"];
32443
32444         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32445             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32446                                 n.nextSibling.ui.getEl(), buf.join(""));
32447         }else{
32448             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32449         }
32450
32451         this.elNode = this.wrap.childNodes[0];
32452         this.ctNode = this.wrap.childNodes[1];
32453         var cs = this.elNode.childNodes;
32454         this.indentNode = cs[0];
32455         this.ecNode = cs[1];
32456         this.iconNode = cs[2];
32457         var index = 3;
32458         if(cb){
32459             this.checkbox = cs[3];
32460             index++;
32461         }
32462         this.anchor = cs[index];
32463         this.textNode = cs[index].firstChild;
32464     },
32465
32466     getAnchor : function(){
32467         return this.anchor;
32468     },
32469
32470     getTextEl : function(){
32471         return this.textNode;
32472     },
32473
32474     getIconEl : function(){
32475         return this.iconNode;
32476     },
32477
32478     isChecked : function(){
32479         return this.checkbox ? this.checkbox.checked : false;
32480     },
32481
32482     updateExpandIcon : function(){
32483         if(this.rendered){
32484             var n = this.node, c1, c2;
32485             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32486             var hasChild = n.hasChildNodes();
32487             if(hasChild){
32488                 if(n.expanded){
32489                     cls += "-minus";
32490                     c1 = "x-tree-node-collapsed";
32491                     c2 = "x-tree-node-expanded";
32492                 }else{
32493                     cls += "-plus";
32494                     c1 = "x-tree-node-expanded";
32495                     c2 = "x-tree-node-collapsed";
32496                 }
32497                 if(this.wasLeaf){
32498                     this.removeClass("x-tree-node-leaf");
32499                     this.wasLeaf = false;
32500                 }
32501                 if(this.c1 != c1 || this.c2 != c2){
32502                     Roo.fly(this.elNode).replaceClass(c1, c2);
32503                     this.c1 = c1; this.c2 = c2;
32504                 }
32505             }else{
32506                 if(!this.wasLeaf){
32507                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32508                     delete this.c1;
32509                     delete this.c2;
32510                     this.wasLeaf = true;
32511                 }
32512             }
32513             var ecc = "x-tree-ec-icon "+cls;
32514             if(this.ecc != ecc){
32515                 this.ecNode.className = ecc;
32516                 this.ecc = ecc;
32517             }
32518         }
32519     },
32520
32521     getChildIndent : function(){
32522         if(!this.childIndent){
32523             var buf = [];
32524             var p = this.node;
32525             while(p){
32526                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32527                     if(!p.isLast()) {
32528                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32529                     } else {
32530                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32531                     }
32532                 }
32533                 p = p.parentNode;
32534             }
32535             this.childIndent = buf.join("");
32536         }
32537         return this.childIndent;
32538     },
32539
32540     renderIndent : function(){
32541         if(this.rendered){
32542             var indent = "";
32543             var p = this.node.parentNode;
32544             if(p){
32545                 indent = p.ui.getChildIndent();
32546             }
32547             if(this.indentMarkup != indent){ // don't rerender if not required
32548                 this.indentNode.innerHTML = indent;
32549                 this.indentMarkup = indent;
32550             }
32551             this.updateExpandIcon();
32552         }
32553     }
32554 };
32555
32556 Roo.tree.RootTreeNodeUI = function(){
32557     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32558 };
32559 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32560     render : function(){
32561         if(!this.rendered){
32562             var targetNode = this.node.ownerTree.innerCt.dom;
32563             this.node.expanded = true;
32564             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32565             this.wrap = this.ctNode = targetNode.firstChild;
32566         }
32567     },
32568     collapse : function(){
32569     },
32570     expand : function(){
32571     }
32572 });/*
32573  * Based on:
32574  * Ext JS Library 1.1.1
32575  * Copyright(c) 2006-2007, Ext JS, LLC.
32576  *
32577  * Originally Released Under LGPL - original licence link has changed is not relivant.
32578  *
32579  * Fork - LGPL
32580  * <script type="text/javascript">
32581  */
32582 /**
32583  * @class Roo.tree.TreeLoader
32584  * @extends Roo.util.Observable
32585  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32586  * nodes from a specified URL. The response must be a javascript Array definition
32587  * who's elements are node definition objects. eg:
32588  * <pre><code>
32589    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32590     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32591 </code></pre>
32592  * <br><br>
32593  * A server request is sent, and child nodes are loaded only when a node is expanded.
32594  * The loading node's id is passed to the server under the parameter name "node" to
32595  * enable the server to produce the correct child nodes.
32596  * <br><br>
32597  * To pass extra parameters, an event handler may be attached to the "beforeload"
32598  * event, and the parameters specified in the TreeLoader's baseParams property:
32599  * <pre><code>
32600     myTreeLoader.on("beforeload", function(treeLoader, node) {
32601         this.baseParams.category = node.attributes.category;
32602     }, this);
32603 </code></pre><
32604  * This would pass an HTTP parameter called "category" to the server containing
32605  * the value of the Node's "category" attribute.
32606  * @constructor
32607  * Creates a new Treeloader.
32608  * @param {Object} config A config object containing config properties.
32609  */
32610 Roo.tree.TreeLoader = function(config){
32611     this.baseParams = {};
32612     this.requestMethod = "POST";
32613     Roo.apply(this, config);
32614
32615     this.addEvents({
32616     
32617         /**
32618          * @event beforeload
32619          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32620          * @param {Object} This TreeLoader object.
32621          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32622          * @param {Object} callback The callback function specified in the {@link #load} call.
32623          */
32624         beforeload : true,
32625         /**
32626          * @event load
32627          * Fires when the node has been successfuly loaded.
32628          * @param {Object} This TreeLoader object.
32629          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32630          * @param {Object} response The response object containing the data from the server.
32631          */
32632         load : true,
32633         /**
32634          * @event loadexception
32635          * Fires if the network request failed.
32636          * @param {Object} This TreeLoader object.
32637          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32638          * @param {Object} response The response object containing the data from the server.
32639          */
32640         loadexception : true,
32641         /**
32642          * @event create
32643          * Fires before a node is created, enabling you to return custom Node types 
32644          * @param {Object} This TreeLoader object.
32645          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32646          */
32647         create : true
32648     });
32649
32650     Roo.tree.TreeLoader.superclass.constructor.call(this);
32651 };
32652
32653 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32654     /**
32655     * @cfg {String} dataUrl The URL from which to request a Json string which
32656     * specifies an array of node definition object representing the child nodes
32657     * to be loaded.
32658     */
32659     /**
32660     * @cfg {Object} baseParams (optional) An object containing properties which
32661     * specify HTTP parameters to be passed to each request for child nodes.
32662     */
32663     /**
32664     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32665     * created by this loader. If the attributes sent by the server have an attribute in this object,
32666     * they take priority.
32667     */
32668     /**
32669     * @cfg {Object} uiProviders (optional) An object containing properties which
32670     * 
32671     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32672     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32673     * <i>uiProvider</i> attribute of a returned child node is a string rather
32674     * than a reference to a TreeNodeUI implementation, this that string value
32675     * is used as a property name in the uiProviders object. You can define the provider named
32676     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32677     */
32678     uiProviders : {},
32679
32680     /**
32681     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32682     * child nodes before loading.
32683     */
32684     clearOnLoad : true,
32685
32686     /**
32687     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32688     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32689     * Grid query { data : [ .....] }
32690     */
32691     
32692     root : false,
32693      /**
32694     * @cfg {String} queryParam (optional) 
32695     * Name of the query as it will be passed on the querystring (defaults to 'node')
32696     * eg. the request will be ?node=[id]
32697     */
32698     
32699     
32700     queryParam: false,
32701     
32702     /**
32703      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32704      * This is called automatically when a node is expanded, but may be used to reload
32705      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32706      * @param {Roo.tree.TreeNode} node
32707      * @param {Function} callback
32708      */
32709     load : function(node, callback){
32710         if(this.clearOnLoad){
32711             while(node.firstChild){
32712                 node.removeChild(node.firstChild);
32713             }
32714         }
32715         if(node.attributes.children){ // preloaded json children
32716             var cs = node.attributes.children;
32717             for(var i = 0, len = cs.length; i < len; i++){
32718                 node.appendChild(this.createNode(cs[i]));
32719             }
32720             if(typeof callback == "function"){
32721                 callback();
32722             }
32723         }else if(this.dataUrl){
32724             this.requestData(node, callback);
32725         }
32726     },
32727
32728     getParams: function(node){
32729         var buf = [], bp = this.baseParams;
32730         for(var key in bp){
32731             if(typeof bp[key] != "function"){
32732                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32733             }
32734         }
32735         var n = this.queryParam === false ? 'node' : this.queryParam;
32736         buf.push(n + "=", encodeURIComponent(node.id));
32737         return buf.join("");
32738     },
32739
32740     requestData : function(node, callback){
32741         if(this.fireEvent("beforeload", this, node, callback) !== false){
32742             this.transId = Roo.Ajax.request({
32743                 method:this.requestMethod,
32744                 url: this.dataUrl||this.url,
32745                 success: this.handleResponse,
32746                 failure: this.handleFailure,
32747                 scope: this,
32748                 argument: {callback: callback, node: node},
32749                 params: this.getParams(node)
32750             });
32751         }else{
32752             // if the load is cancelled, make sure we notify
32753             // the node that we are done
32754             if(typeof callback == "function"){
32755                 callback();
32756             }
32757         }
32758     },
32759
32760     isLoading : function(){
32761         return this.transId ? true : false;
32762     },
32763
32764     abort : function(){
32765         if(this.isLoading()){
32766             Roo.Ajax.abort(this.transId);
32767         }
32768     },
32769
32770     // private
32771     createNode : function(attr){
32772         // apply baseAttrs, nice idea Corey!
32773         if(this.baseAttrs){
32774             Roo.applyIf(attr, this.baseAttrs);
32775         }
32776         if(this.applyLoader !== false){
32777             attr.loader = this;
32778         }
32779         // uiProvider = depreciated..
32780         
32781         if(typeof(attr.uiProvider) == 'string'){
32782            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32783                 /**  eval:var:attr */ eval(attr.uiProvider);
32784         }
32785         if(typeof(this.uiProviders['default']) != 'undefined') {
32786             attr.uiProvider = this.uiProviders['default'];
32787         }
32788         
32789         this.fireEvent('create', this, attr);
32790         
32791         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32792         return(attr.leaf ?
32793                         new Roo.tree.TreeNode(attr) :
32794                         new Roo.tree.AsyncTreeNode(attr));
32795     },
32796
32797     processResponse : function(response, node, callback){
32798         var json = response.responseText;
32799         try {
32800             
32801             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32802             if (this.root !== false) {
32803                 o = o[this.root];
32804             }
32805             
32806             for(var i = 0, len = o.length; i < len; i++){
32807                 var n = this.createNode(o[i]);
32808                 if(n){
32809                     node.appendChild(n);
32810                 }
32811             }
32812             if(typeof callback == "function"){
32813                 callback(this, node);
32814             }
32815         }catch(e){
32816             this.handleFailure(response);
32817         }
32818     },
32819
32820     handleResponse : function(response){
32821         this.transId = false;
32822         var a = response.argument;
32823         this.processResponse(response, a.node, a.callback);
32824         this.fireEvent("load", this, a.node, response);
32825     },
32826
32827     handleFailure : function(response){
32828         this.transId = false;
32829         var a = response.argument;
32830         this.fireEvent("loadexception", this, a.node, response);
32831         if(typeof a.callback == "function"){
32832             a.callback(this, a.node);
32833         }
32834     }
32835 });/*
32836  * Based on:
32837  * Ext JS Library 1.1.1
32838  * Copyright(c) 2006-2007, Ext JS, LLC.
32839  *
32840  * Originally Released Under LGPL - original licence link has changed is not relivant.
32841  *
32842  * Fork - LGPL
32843  * <script type="text/javascript">
32844  */
32845
32846 /**
32847 * @class Roo.tree.TreeFilter
32848 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32849 * @param {TreePanel} tree
32850 * @param {Object} config (optional)
32851  */
32852 Roo.tree.TreeFilter = function(tree, config){
32853     this.tree = tree;
32854     this.filtered = {};
32855     Roo.apply(this, config);
32856 };
32857
32858 Roo.tree.TreeFilter.prototype = {
32859     clearBlank:false,
32860     reverse:false,
32861     autoClear:false,
32862     remove:false,
32863
32864      /**
32865      * Filter the data by a specific attribute.
32866      * @param {String/RegExp} value Either string that the attribute value
32867      * should start with or a RegExp to test against the attribute
32868      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32869      * @param {TreeNode} startNode (optional) The node to start the filter at.
32870      */
32871     filter : function(value, attr, startNode){
32872         attr = attr || "text";
32873         var f;
32874         if(typeof value == "string"){
32875             var vlen = value.length;
32876             // auto clear empty filter
32877             if(vlen == 0 && this.clearBlank){
32878                 this.clear();
32879                 return;
32880             }
32881             value = value.toLowerCase();
32882             f = function(n){
32883                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32884             };
32885         }else if(value.exec){ // regex?
32886             f = function(n){
32887                 return value.test(n.attributes[attr]);
32888             };
32889         }else{
32890             throw 'Illegal filter type, must be string or regex';
32891         }
32892         this.filterBy(f, null, startNode);
32893         },
32894
32895     /**
32896      * Filter by a function. The passed function will be called with each
32897      * node in the tree (or from the startNode). If the function returns true, the node is kept
32898      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32899      * @param {Function} fn The filter function
32900      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32901      */
32902     filterBy : function(fn, scope, startNode){
32903         startNode = startNode || this.tree.root;
32904         if(this.autoClear){
32905             this.clear();
32906         }
32907         var af = this.filtered, rv = this.reverse;
32908         var f = function(n){
32909             if(n == startNode){
32910                 return true;
32911             }
32912             if(af[n.id]){
32913                 return false;
32914             }
32915             var m = fn.call(scope || n, n);
32916             if(!m || rv){
32917                 af[n.id] = n;
32918                 n.ui.hide();
32919                 return false;
32920             }
32921             return true;
32922         };
32923         startNode.cascade(f);
32924         if(this.remove){
32925            for(var id in af){
32926                if(typeof id != "function"){
32927                    var n = af[id];
32928                    if(n && n.parentNode){
32929                        n.parentNode.removeChild(n);
32930                    }
32931                }
32932            }
32933         }
32934     },
32935
32936     /**
32937      * Clears the current filter. Note: with the "remove" option
32938      * set a filter cannot be cleared.
32939      */
32940     clear : function(){
32941         var t = this.tree;
32942         var af = this.filtered;
32943         for(var id in af){
32944             if(typeof id != "function"){
32945                 var n = af[id];
32946                 if(n){
32947                     n.ui.show();
32948                 }
32949             }
32950         }
32951         this.filtered = {};
32952     }
32953 };
32954 /*
32955  * Based on:
32956  * Ext JS Library 1.1.1
32957  * Copyright(c) 2006-2007, Ext JS, LLC.
32958  *
32959  * Originally Released Under LGPL - original licence link has changed is not relivant.
32960  *
32961  * Fork - LGPL
32962  * <script type="text/javascript">
32963  */
32964  
32965
32966 /**
32967  * @class Roo.tree.TreeSorter
32968  * Provides sorting of nodes in a TreePanel
32969  * 
32970  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32971  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32972  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32973  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32974  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32975  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32976  * @constructor
32977  * @param {TreePanel} tree
32978  * @param {Object} config
32979  */
32980 Roo.tree.TreeSorter = function(tree, config){
32981     Roo.apply(this, config);
32982     tree.on("beforechildrenrendered", this.doSort, this);
32983     tree.on("append", this.updateSort, this);
32984     tree.on("insert", this.updateSort, this);
32985     
32986     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32987     var p = this.property || "text";
32988     var sortType = this.sortType;
32989     var fs = this.folderSort;
32990     var cs = this.caseSensitive === true;
32991     var leafAttr = this.leafAttr || 'leaf';
32992
32993     this.sortFn = function(n1, n2){
32994         if(fs){
32995             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32996                 return 1;
32997             }
32998             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32999                 return -1;
33000             }
33001         }
33002         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33003         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33004         if(v1 < v2){
33005                         return dsc ? +1 : -1;
33006                 }else if(v1 > v2){
33007                         return dsc ? -1 : +1;
33008         }else{
33009                 return 0;
33010         }
33011     };
33012 };
33013
33014 Roo.tree.TreeSorter.prototype = {
33015     doSort : function(node){
33016         node.sort(this.sortFn);
33017     },
33018     
33019     compareNodes : function(n1, n2){
33020         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33021     },
33022     
33023     updateSort : function(tree, node){
33024         if(node.childrenRendered){
33025             this.doSort.defer(1, this, [node]);
33026         }
33027     }
33028 };/*
33029  * Based on:
33030  * Ext JS Library 1.1.1
33031  * Copyright(c) 2006-2007, Ext JS, LLC.
33032  *
33033  * Originally Released Under LGPL - original licence link has changed is not relivant.
33034  *
33035  * Fork - LGPL
33036  * <script type="text/javascript">
33037  */
33038
33039 if(Roo.dd.DropZone){
33040     
33041 Roo.tree.TreeDropZone = function(tree, config){
33042     this.allowParentInsert = false;
33043     this.allowContainerDrop = false;
33044     this.appendOnly = false;
33045     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33046     this.tree = tree;
33047     this.lastInsertClass = "x-tree-no-status";
33048     this.dragOverData = {};
33049 };
33050
33051 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33052     ddGroup : "TreeDD",
33053     
33054     expandDelay : 1000,
33055     
33056     expandNode : function(node){
33057         if(node.hasChildNodes() && !node.isExpanded()){
33058             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33059         }
33060     },
33061     
33062     queueExpand : function(node){
33063         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33064     },
33065     
33066     cancelExpand : function(){
33067         if(this.expandProcId){
33068             clearTimeout(this.expandProcId);
33069             this.expandProcId = false;
33070         }
33071     },
33072     
33073     isValidDropPoint : function(n, pt, dd, e, data){
33074         if(!n || !data){ return false; }
33075         var targetNode = n.node;
33076         var dropNode = data.node;
33077         // default drop rules
33078         if(!(targetNode && targetNode.isTarget && pt)){
33079             return false;
33080         }
33081         if(pt == "append" && targetNode.allowChildren === false){
33082             return false;
33083         }
33084         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33085             return false;
33086         }
33087         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33088             return false;
33089         }
33090         // reuse the object
33091         var overEvent = this.dragOverData;
33092         overEvent.tree = this.tree;
33093         overEvent.target = targetNode;
33094         overEvent.data = data;
33095         overEvent.point = pt;
33096         overEvent.source = dd;
33097         overEvent.rawEvent = e;
33098         overEvent.dropNode = dropNode;
33099         overEvent.cancel = false;  
33100         var result = this.tree.fireEvent("nodedragover", overEvent);
33101         return overEvent.cancel === false && result !== false;
33102     },
33103     
33104     getDropPoint : function(e, n, dd){
33105         var tn = n.node;
33106         if(tn.isRoot){
33107             return tn.allowChildren !== false ? "append" : false; // always append for root
33108         }
33109         var dragEl = n.ddel;
33110         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33111         var y = Roo.lib.Event.getPageY(e);
33112         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33113         
33114         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33115         var noAppend = tn.allowChildren === false;
33116         if(this.appendOnly || tn.parentNode.allowChildren === false){
33117             return noAppend ? false : "append";
33118         }
33119         var noBelow = false;
33120         if(!this.allowParentInsert){
33121             noBelow = tn.hasChildNodes() && tn.isExpanded();
33122         }
33123         var q = (b - t) / (noAppend ? 2 : 3);
33124         if(y >= t && y < (t + q)){
33125             return "above";
33126         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33127             return "below";
33128         }else{
33129             return "append";
33130         }
33131     },
33132     
33133     onNodeEnter : function(n, dd, e, data){
33134         this.cancelExpand();
33135     },
33136     
33137     onNodeOver : function(n, dd, e, data){
33138         var pt = this.getDropPoint(e, n, dd);
33139         var node = n.node;
33140         
33141         // auto node expand check
33142         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33143             this.queueExpand(node);
33144         }else if(pt != "append"){
33145             this.cancelExpand();
33146         }
33147         
33148         // set the insert point style on the target node
33149         var returnCls = this.dropNotAllowed;
33150         if(this.isValidDropPoint(n, pt, dd, e, data)){
33151            if(pt){
33152                var el = n.ddel;
33153                var cls;
33154                if(pt == "above"){
33155                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33156                    cls = "x-tree-drag-insert-above";
33157                }else if(pt == "below"){
33158                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33159                    cls = "x-tree-drag-insert-below";
33160                }else{
33161                    returnCls = "x-tree-drop-ok-append";
33162                    cls = "x-tree-drag-append";
33163                }
33164                if(this.lastInsertClass != cls){
33165                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33166                    this.lastInsertClass = cls;
33167                }
33168            }
33169        }
33170        return returnCls;
33171     },
33172     
33173     onNodeOut : function(n, dd, e, data){
33174         this.cancelExpand();
33175         this.removeDropIndicators(n);
33176     },
33177     
33178     onNodeDrop : function(n, dd, e, data){
33179         var point = this.getDropPoint(e, n, dd);
33180         var targetNode = n.node;
33181         targetNode.ui.startDrop();
33182         if(!this.isValidDropPoint(n, point, dd, e, data)){
33183             targetNode.ui.endDrop();
33184             return false;
33185         }
33186         // first try to find the drop node
33187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33188         var dropEvent = {
33189             tree : this.tree,
33190             target: targetNode,
33191             data: data,
33192             point: point,
33193             source: dd,
33194             rawEvent: e,
33195             dropNode: dropNode,
33196             cancel: !dropNode   
33197         };
33198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33200             targetNode.ui.endDrop();
33201             return false;
33202         }
33203         // allow target changing
33204         targetNode = dropEvent.target;
33205         if(point == "append" && !targetNode.isExpanded()){
33206             targetNode.expand(false, null, function(){
33207                 this.completeDrop(dropEvent);
33208             }.createDelegate(this));
33209         }else{
33210             this.completeDrop(dropEvent);
33211         }
33212         return true;
33213     },
33214     
33215     completeDrop : function(de){
33216         var ns = de.dropNode, p = de.point, t = de.target;
33217         if(!(ns instanceof Array)){
33218             ns = [ns];
33219         }
33220         var n;
33221         for(var i = 0, len = ns.length; i < len; i++){
33222             n = ns[i];
33223             if(p == "above"){
33224                 t.parentNode.insertBefore(n, t);
33225             }else if(p == "below"){
33226                 t.parentNode.insertBefore(n, t.nextSibling);
33227             }else{
33228                 t.appendChild(n);
33229             }
33230         }
33231         n.ui.focus();
33232         if(this.tree.hlDrop){
33233             n.ui.highlight();
33234         }
33235         t.ui.endDrop();
33236         this.tree.fireEvent("nodedrop", de);
33237     },
33238     
33239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33240         if(this.tree.hlDrop){
33241             dropNode.ui.focus();
33242             dropNode.ui.highlight();
33243         }
33244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33245     },
33246     
33247     getTree : function(){
33248         return this.tree;
33249     },
33250     
33251     removeDropIndicators : function(n){
33252         if(n && n.ddel){
33253             var el = n.ddel;
33254             Roo.fly(el).removeClass([
33255                     "x-tree-drag-insert-above",
33256                     "x-tree-drag-insert-below",
33257                     "x-tree-drag-append"]);
33258             this.lastInsertClass = "_noclass";
33259         }
33260     },
33261     
33262     beforeDragDrop : function(target, e, id){
33263         this.cancelExpand();
33264         return true;
33265     },
33266     
33267     afterRepair : function(data){
33268         if(data && Roo.enableFx){
33269             data.node.ui.highlight();
33270         }
33271         this.hideProxy();
33272     }    
33273 });
33274
33275 }
33276 /*
33277  * Based on:
33278  * Ext JS Library 1.1.1
33279  * Copyright(c) 2006-2007, Ext JS, LLC.
33280  *
33281  * Originally Released Under LGPL - original licence link has changed is not relivant.
33282  *
33283  * Fork - LGPL
33284  * <script type="text/javascript">
33285  */
33286  
33287
33288 if(Roo.dd.DragZone){
33289 Roo.tree.TreeDragZone = function(tree, config){
33290     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33291     this.tree = tree;
33292 };
33293
33294 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33295     ddGroup : "TreeDD",
33296     
33297     onBeforeDrag : function(data, e){
33298         var n = data.node;
33299         return n && n.draggable && !n.disabled;
33300     },
33301     
33302     onInitDrag : function(e){
33303         var data = this.dragData;
33304         this.tree.getSelectionModel().select(data.node);
33305         this.proxy.update("");
33306         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33307         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33308     },
33309     
33310     getRepairXY : function(e, data){
33311         return data.node.ui.getDDRepairXY();
33312     },
33313     
33314     onEndDrag : function(data, e){
33315         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33316     },
33317     
33318     onValidDrop : function(dd, e, id){
33319         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33320         this.hideProxy();
33321     },
33322     
33323     beforeInvalidDrop : function(e, id){
33324         // this scrolls the original position back into view
33325         var sm = this.tree.getSelectionModel();
33326         sm.clearSelections();
33327         sm.select(this.dragData.node);
33328     }
33329 });
33330 }/*
33331  * Based on:
33332  * Ext JS Library 1.1.1
33333  * Copyright(c) 2006-2007, Ext JS, LLC.
33334  *
33335  * Originally Released Under LGPL - original licence link has changed is not relivant.
33336  *
33337  * Fork - LGPL
33338  * <script type="text/javascript">
33339  */
33340 /**
33341  * @class Roo.tree.TreeEditor
33342  * @extends Roo.Editor
33343  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33344  * as the editor field.
33345  * @constructor
33346  * @param {TreePanel} tree
33347  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33348  */
33349 Roo.tree.TreeEditor = function(tree, config){
33350     config = config || {};
33351     var field = config.events ? config : new Roo.form.TextField(config);
33352     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33353
33354     this.tree = tree;
33355
33356     tree.on('beforeclick', this.beforeNodeClick, this);
33357     tree.getTreeEl().on('mousedown', this.hide, this);
33358     this.on('complete', this.updateNode, this);
33359     this.on('beforestartedit', this.fitToTree, this);
33360     this.on('startedit', this.bindScroll, this, {delay:10});
33361     this.on('specialkey', this.onSpecialKey, this);
33362 };
33363
33364 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33365     /**
33366      * @cfg {String} alignment
33367      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33368      */
33369     alignment: "l-l",
33370     // inherit
33371     autoSize: false,
33372     /**
33373      * @cfg {Boolean} hideEl
33374      * True to hide the bound element while the editor is displayed (defaults to false)
33375      */
33376     hideEl : false,
33377     /**
33378      * @cfg {String} cls
33379      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33380      */
33381     cls: "x-small-editor x-tree-editor",
33382     /**
33383      * @cfg {Boolean} shim
33384      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33385      */
33386     shim:false,
33387     // inherit
33388     shadow:"frame",
33389     /**
33390      * @cfg {Number} maxWidth
33391      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33392      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33393      * scroll and client offsets into account prior to each edit.
33394      */
33395     maxWidth: 250,
33396
33397     editDelay : 350,
33398
33399     // private
33400     fitToTree : function(ed, el){
33401         var td = this.tree.getTreeEl().dom, nd = el.dom;
33402         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33403             td.scrollLeft = nd.offsetLeft;
33404         }
33405         var w = Math.min(
33406                 this.maxWidth,
33407                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33408         this.setSize(w, '');
33409     },
33410
33411     // private
33412     triggerEdit : function(node){
33413         this.completeEdit();
33414         this.editNode = node;
33415         this.startEdit(node.ui.textNode, node.text);
33416     },
33417
33418     // private
33419     bindScroll : function(){
33420         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33421     },
33422
33423     // private
33424     beforeNodeClick : function(node, e){
33425         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33426         this.lastClick = new Date();
33427         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33428             e.stopEvent();
33429             this.triggerEdit(node);
33430             return false;
33431         }
33432     },
33433
33434     // private
33435     updateNode : function(ed, value){
33436         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33437         this.editNode.setText(value);
33438     },
33439
33440     // private
33441     onHide : function(){
33442         Roo.tree.TreeEditor.superclass.onHide.call(this);
33443         if(this.editNode){
33444             this.editNode.ui.focus();
33445         }
33446     },
33447
33448     // private
33449     onSpecialKey : function(field, e){
33450         var k = e.getKey();
33451         if(k == e.ESC){
33452             e.stopEvent();
33453             this.cancelEdit();
33454         }else if(k == e.ENTER && !e.hasModifier()){
33455             e.stopEvent();
33456             this.completeEdit();
33457         }
33458     }
33459 });//<Script type="text/javascript">
33460 /*
33461  * Based on:
33462  * Ext JS Library 1.1.1
33463  * Copyright(c) 2006-2007, Ext JS, LLC.
33464  *
33465  * Originally Released Under LGPL - original licence link has changed is not relivant.
33466  *
33467  * Fork - LGPL
33468  * <script type="text/javascript">
33469  */
33470  
33471 /**
33472  * Not documented??? - probably should be...
33473  */
33474
33475 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33476     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33477     
33478     renderElements : function(n, a, targetNode, bulkRender){
33479         //consel.log("renderElements?");
33480         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33481
33482         var t = n.getOwnerTree();
33483         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33484         
33485         var cols = t.columns;
33486         var bw = t.borderWidth;
33487         var c = cols[0];
33488         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33489          var cb = typeof a.checked == "boolean";
33490         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33491         var colcls = 'x-t-' + tid + '-c0';
33492         var buf = [
33493             '<li class="x-tree-node">',
33494             
33495                 
33496                 '<div class="x-tree-node-el ', a.cls,'">',
33497                     // extran...
33498                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33499                 
33500                 
33501                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33502                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33503                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33504                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33505                            (a.iconCls ? ' '+a.iconCls : ''),
33506                            '" unselectable="on" />',
33507                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33508                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33509                              
33510                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33511                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33512                             '<span unselectable="on" qtip="' + tx + '">',
33513                              tx,
33514                              '</span></a>' ,
33515                     '</div>',
33516                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33517                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33518                  ];
33519         for(var i = 1, len = cols.length; i < len; i++){
33520             c = cols[i];
33521             colcls = 'x-t-' + tid + '-c' +i;
33522             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33523             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33524                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33525                       "</div>");
33526          }
33527          
33528          buf.push(
33529             '</a>',
33530             '<div class="x-clear"></div></div>',
33531             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33532             "</li>");
33533         
33534         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33535             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33536                                 n.nextSibling.ui.getEl(), buf.join(""));
33537         }else{
33538             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33539         }
33540         var el = this.wrap.firstChild;
33541         this.elRow = el;
33542         this.elNode = el.firstChild;
33543         this.ranchor = el.childNodes[1];
33544         this.ctNode = this.wrap.childNodes[1];
33545         var cs = el.firstChild.childNodes;
33546         this.indentNode = cs[0];
33547         this.ecNode = cs[1];
33548         this.iconNode = cs[2];
33549         var index = 3;
33550         if(cb){
33551             this.checkbox = cs[3];
33552             index++;
33553         }
33554         this.anchor = cs[index];
33555         
33556         this.textNode = cs[index].firstChild;
33557         
33558         //el.on("click", this.onClick, this);
33559         //el.on("dblclick", this.onDblClick, this);
33560         
33561         
33562        // console.log(this);
33563     },
33564     initEvents : function(){
33565         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33566         
33567             
33568         var a = this.ranchor;
33569
33570         var el = Roo.get(a);
33571
33572         if(Roo.isOpera){ // opera render bug ignores the CSS
33573             el.setStyle("text-decoration", "none");
33574         }
33575
33576         el.on("click", this.onClick, this);
33577         el.on("dblclick", this.onDblClick, this);
33578         el.on("contextmenu", this.onContextMenu, this);
33579         
33580     },
33581     
33582     /*onSelectedChange : function(state){
33583         if(state){
33584             this.focus();
33585             this.addClass("x-tree-selected");
33586         }else{
33587             //this.blur();
33588             this.removeClass("x-tree-selected");
33589         }
33590     },*/
33591     addClass : function(cls){
33592         if(this.elRow){
33593             Roo.fly(this.elRow).addClass(cls);
33594         }
33595         
33596     },
33597     
33598     
33599     removeClass : function(cls){
33600         if(this.elRow){
33601             Roo.fly(this.elRow).removeClass(cls);
33602         }
33603     }
33604
33605     
33606     
33607 });//<Script type="text/javascript">
33608
33609 /*
33610  * Based on:
33611  * Ext JS Library 1.1.1
33612  * Copyright(c) 2006-2007, Ext JS, LLC.
33613  *
33614  * Originally Released Under LGPL - original licence link has changed is not relivant.
33615  *
33616  * Fork - LGPL
33617  * <script type="text/javascript">
33618  */
33619  
33620
33621 /**
33622  * @class Roo.tree.ColumnTree
33623  * @extends Roo.data.TreePanel
33624  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33625  * @cfg {int} borderWidth  compined right/left border allowance
33626  * @constructor
33627  * @param {String/HTMLElement/Element} el The container element
33628  * @param {Object} config
33629  */
33630 Roo.tree.ColumnTree =  function(el, config)
33631 {
33632    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33633    this.addEvents({
33634         /**
33635         * @event resize
33636         * Fire this event on a container when it resizes
33637         * @param {int} w Width
33638         * @param {int} h Height
33639         */
33640        "resize" : true
33641     });
33642     this.on('resize', this.onResize, this);
33643 };
33644
33645 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33646     //lines:false,
33647     
33648     
33649     borderWidth: Roo.isBorderBox ? 0 : 2, 
33650     headEls : false,
33651     
33652     render : function(){
33653         // add the header.....
33654        
33655         Roo.tree.ColumnTree.superclass.render.apply(this);
33656         
33657         this.el.addClass('x-column-tree');
33658         
33659         this.headers = this.el.createChild(
33660             {cls:'x-tree-headers'},this.innerCt.dom);
33661    
33662         var cols = this.columns, c;
33663         var totalWidth = 0;
33664         this.headEls = [];
33665         var  len = cols.length;
33666         for(var i = 0; i < len; i++){
33667              c = cols[i];
33668              totalWidth += c.width;
33669             this.headEls.push(this.headers.createChild({
33670                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33671                  cn: {
33672                      cls:'x-tree-hd-text',
33673                      html: c.header
33674                  },
33675                  style:'width:'+(c.width-this.borderWidth)+'px;'
33676              }));
33677         }
33678         this.headers.createChild({cls:'x-clear'});
33679         // prevent floats from wrapping when clipped
33680         this.headers.setWidth(totalWidth);
33681         //this.innerCt.setWidth(totalWidth);
33682         this.innerCt.setStyle({ overflow: 'auto' });
33683         this.onResize(this.width, this.height);
33684              
33685         
33686     },
33687     onResize : function(w,h)
33688     {
33689         this.height = h;
33690         this.width = w;
33691         // resize cols..
33692         this.innerCt.setWidth(this.width);
33693         this.innerCt.setHeight(this.height-20);
33694         
33695         // headers...
33696         var cols = this.columns, c;
33697         var totalWidth = 0;
33698         var expEl = false;
33699         var len = cols.length;
33700         for(var i = 0; i < len; i++){
33701             c = cols[i];
33702             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33703                 // it's the expander..
33704                 expEl  = this.headEls[i];
33705                 continue;
33706             }
33707             totalWidth += c.width;
33708             
33709         }
33710         if (expEl) {
33711             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33712         }
33713         this.headers.setWidth(w-20);
33714
33715         
33716         
33717         
33718     }
33719 });
33720 /*
33721  * Based on:
33722  * Ext JS Library 1.1.1
33723  * Copyright(c) 2006-2007, Ext JS, LLC.
33724  *
33725  * Originally Released Under LGPL - original licence link has changed is not relivant.
33726  *
33727  * Fork - LGPL
33728  * <script type="text/javascript">
33729  */
33730  
33731 /**
33732  * @class Roo.menu.Menu
33733  * @extends Roo.util.Observable
33734  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33735  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33736  * @constructor
33737  * Creates a new Menu
33738  * @param {Object} config Configuration options
33739  */
33740 Roo.menu.Menu = function(config){
33741     Roo.apply(this, config);
33742     this.id = this.id || Roo.id();
33743     this.addEvents({
33744         /**
33745          * @event beforeshow
33746          * Fires before this menu is displayed
33747          * @param {Roo.menu.Menu} this
33748          */
33749         beforeshow : true,
33750         /**
33751          * @event beforehide
33752          * Fires before this menu is hidden
33753          * @param {Roo.menu.Menu} this
33754          */
33755         beforehide : true,
33756         /**
33757          * @event show
33758          * Fires after this menu is displayed
33759          * @param {Roo.menu.Menu} this
33760          */
33761         show : true,
33762         /**
33763          * @event hide
33764          * Fires after this menu is hidden
33765          * @param {Roo.menu.Menu} this
33766          */
33767         hide : true,
33768         /**
33769          * @event click
33770          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33771          * @param {Roo.menu.Menu} this
33772          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33773          * @param {Roo.EventObject} e
33774          */
33775         click : true,
33776         /**
33777          * @event mouseover
33778          * Fires when the mouse is hovering over this menu
33779          * @param {Roo.menu.Menu} this
33780          * @param {Roo.EventObject} e
33781          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33782          */
33783         mouseover : true,
33784         /**
33785          * @event mouseout
33786          * Fires when the mouse exits this menu
33787          * @param {Roo.menu.Menu} this
33788          * @param {Roo.EventObject} e
33789          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33790          */
33791         mouseout : true,
33792         /**
33793          * @event itemclick
33794          * Fires when a menu item contained in this menu is clicked
33795          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33796          * @param {Roo.EventObject} e
33797          */
33798         itemclick: true
33799     });
33800     if (this.registerMenu) {
33801         Roo.menu.MenuMgr.register(this);
33802     }
33803     
33804     var mis = this.items;
33805     this.items = new Roo.util.MixedCollection();
33806     if(mis){
33807         this.add.apply(this, mis);
33808     }
33809 };
33810
33811 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33812     /**
33813      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33814      */
33815     minWidth : 120,
33816     /**
33817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33818      * for bottom-right shadow (defaults to "sides")
33819      */
33820     shadow : "sides",
33821     /**
33822      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33823      * this menu (defaults to "tl-tr?")
33824      */
33825     subMenuAlign : "tl-tr?",
33826     /**
33827      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33828      * relative to its element of origin (defaults to "tl-bl?")
33829      */
33830     defaultAlign : "tl-bl?",
33831     /**
33832      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33833      */
33834     allowOtherMenus : false,
33835     /**
33836      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33837      */
33838     registerMenu : true,
33839
33840     hidden:true,
33841
33842     // private
33843     render : function(){
33844         if(this.el){
33845             return;
33846         }
33847         var el = this.el = new Roo.Layer({
33848             cls: "x-menu",
33849             shadow:this.shadow,
33850             constrain: false,
33851             parentEl: this.parentEl || document.body,
33852             zindex:15000
33853         });
33854
33855         this.keyNav = new Roo.menu.MenuNav(this);
33856
33857         if(this.plain){
33858             el.addClass("x-menu-plain");
33859         }
33860         if(this.cls){
33861             el.addClass(this.cls);
33862         }
33863         // generic focus element
33864         this.focusEl = el.createChild({
33865             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33866         });
33867         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33868         ul.on("click", this.onClick, this);
33869         ul.on("mouseover", this.onMouseOver, this);
33870         ul.on("mouseout", this.onMouseOut, this);
33871         this.items.each(function(item){
33872             var li = document.createElement("li");
33873             li.className = "x-menu-list-item";
33874             ul.dom.appendChild(li);
33875             item.render(li, this);
33876         }, this);
33877         this.ul = ul;
33878         this.autoWidth();
33879     },
33880
33881     // private
33882     autoWidth : function(){
33883         var el = this.el, ul = this.ul;
33884         if(!el){
33885             return;
33886         }
33887         var w = this.width;
33888         if(w){
33889             el.setWidth(w);
33890         }else if(Roo.isIE){
33891             el.setWidth(this.minWidth);
33892             var t = el.dom.offsetWidth; // force recalc
33893             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33894         }
33895     },
33896
33897     // private
33898     delayAutoWidth : function(){
33899         if(this.rendered){
33900             if(!this.awTask){
33901                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33902             }
33903             this.awTask.delay(20);
33904         }
33905     },
33906
33907     // private
33908     findTargetItem : function(e){
33909         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33910         if(t && t.menuItemId){
33911             return this.items.get(t.menuItemId);
33912         }
33913     },
33914
33915     // private
33916     onClick : function(e){
33917         var t;
33918         if(t = this.findTargetItem(e)){
33919             t.onClick(e);
33920             this.fireEvent("click", this, t, e);
33921         }
33922     },
33923
33924     // private
33925     setActiveItem : function(item, autoExpand){
33926         if(item != this.activeItem){
33927             if(this.activeItem){
33928                 this.activeItem.deactivate();
33929             }
33930             this.activeItem = item;
33931             item.activate(autoExpand);
33932         }else if(autoExpand){
33933             item.expandMenu();
33934         }
33935     },
33936
33937     // private
33938     tryActivate : function(start, step){
33939         var items = this.items;
33940         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33941             var item = items.get(i);
33942             if(!item.disabled && item.canActivate){
33943                 this.setActiveItem(item, false);
33944                 return item;
33945             }
33946         }
33947         return false;
33948     },
33949
33950     // private
33951     onMouseOver : function(e){
33952         var t;
33953         if(t = this.findTargetItem(e)){
33954             if(t.canActivate && !t.disabled){
33955                 this.setActiveItem(t, true);
33956             }
33957         }
33958         this.fireEvent("mouseover", this, e, t);
33959     },
33960
33961     // private
33962     onMouseOut : function(e){
33963         var t;
33964         if(t = this.findTargetItem(e)){
33965             if(t == this.activeItem && t.shouldDeactivate(e)){
33966                 this.activeItem.deactivate();
33967                 delete this.activeItem;
33968             }
33969         }
33970         this.fireEvent("mouseout", this, e, t);
33971     },
33972
33973     /**
33974      * Read-only.  Returns true if the menu is currently displayed, else false.
33975      * @type Boolean
33976      */
33977     isVisible : function(){
33978         return this.el && !this.hidden;
33979     },
33980
33981     /**
33982      * Displays this menu relative to another element
33983      * @param {String/HTMLElement/Roo.Element} element The element to align to
33984      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33985      * the element (defaults to this.defaultAlign)
33986      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33987      */
33988     show : function(el, pos, parentMenu){
33989         this.parentMenu = parentMenu;
33990         if(!this.el){
33991             this.render();
33992         }
33993         this.fireEvent("beforeshow", this);
33994         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33995     },
33996
33997     /**
33998      * Displays this menu at a specific xy position
33999      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34000      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34001      */
34002     showAt : function(xy, parentMenu, /* private: */_e){
34003         this.parentMenu = parentMenu;
34004         if(!this.el){
34005             this.render();
34006         }
34007         if(_e !== false){
34008             this.fireEvent("beforeshow", this);
34009             xy = this.el.adjustForConstraints(xy);
34010         }
34011         this.el.setXY(xy);
34012         this.el.show();
34013         this.hidden = false;
34014         this.focus();
34015         this.fireEvent("show", this);
34016     },
34017
34018     focus : function(){
34019         if(!this.hidden){
34020             this.doFocus.defer(50, this);
34021         }
34022     },
34023
34024     doFocus : function(){
34025         if(!this.hidden){
34026             this.focusEl.focus();
34027         }
34028     },
34029
34030     /**
34031      * Hides this menu and optionally all parent menus
34032      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34033      */
34034     hide : function(deep){
34035         if(this.el && this.isVisible()){
34036             this.fireEvent("beforehide", this);
34037             if(this.activeItem){
34038                 this.activeItem.deactivate();
34039                 this.activeItem = null;
34040             }
34041             this.el.hide();
34042             this.hidden = true;
34043             this.fireEvent("hide", this);
34044         }
34045         if(deep === true && this.parentMenu){
34046             this.parentMenu.hide(true);
34047         }
34048     },
34049
34050     /**
34051      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34052      * Any of the following are valid:
34053      * <ul>
34054      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34055      * <li>An HTMLElement object which will be converted to a menu item</li>
34056      * <li>A menu item config object that will be created as a new menu item</li>
34057      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34058      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34059      * </ul>
34060      * Usage:
34061      * <pre><code>
34062 // Create the menu
34063 var menu = new Roo.menu.Menu();
34064
34065 // Create a menu item to add by reference
34066 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34067
34068 // Add a bunch of items at once using different methods.
34069 // Only the last item added will be returned.
34070 var item = menu.add(
34071     menuItem,                // add existing item by ref
34072     'Dynamic Item',          // new TextItem
34073     '-',                     // new separator
34074     { text: 'Config Item' }  // new item by config
34075 );
34076 </code></pre>
34077      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34078      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34079      */
34080     add : function(){
34081         var a = arguments, l = a.length, item;
34082         for(var i = 0; i < l; i++){
34083             var el = a[i];
34084             if ((typeof(el) == "object") && el.xtype && el.xns) {
34085                 el = Roo.factory(el, Roo.menu);
34086             }
34087             
34088             if(el.render){ // some kind of Item
34089                 item = this.addItem(el);
34090             }else if(typeof el == "string"){ // string
34091                 if(el == "separator" || el == "-"){
34092                     item = this.addSeparator();
34093                 }else{
34094                     item = this.addText(el);
34095                 }
34096             }else if(el.tagName || el.el){ // element
34097                 item = this.addElement(el);
34098             }else if(typeof el == "object"){ // must be menu item config?
34099                 item = this.addMenuItem(el);
34100             }
34101         }
34102         return item;
34103     },
34104
34105     /**
34106      * Returns this menu's underlying {@link Roo.Element} object
34107      * @return {Roo.Element} The element
34108      */
34109     getEl : function(){
34110         if(!this.el){
34111             this.render();
34112         }
34113         return this.el;
34114     },
34115
34116     /**
34117      * Adds a separator bar to the menu
34118      * @return {Roo.menu.Item} The menu item that was added
34119      */
34120     addSeparator : function(){
34121         return this.addItem(new Roo.menu.Separator());
34122     },
34123
34124     /**
34125      * Adds an {@link Roo.Element} object to the menu
34126      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34127      * @return {Roo.menu.Item} The menu item that was added
34128      */
34129     addElement : function(el){
34130         return this.addItem(new Roo.menu.BaseItem(el));
34131     },
34132
34133     /**
34134      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34135      * @param {Roo.menu.Item} item The menu item to add
34136      * @return {Roo.menu.Item} The menu item that was added
34137      */
34138     addItem : function(item){
34139         this.items.add(item);
34140         if(this.ul){
34141             var li = document.createElement("li");
34142             li.className = "x-menu-list-item";
34143             this.ul.dom.appendChild(li);
34144             item.render(li, this);
34145             this.delayAutoWidth();
34146         }
34147         return item;
34148     },
34149
34150     /**
34151      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34152      * @param {Object} config A MenuItem config object
34153      * @return {Roo.menu.Item} The menu item that was added
34154      */
34155     addMenuItem : function(config){
34156         if(!(config instanceof Roo.menu.Item)){
34157             if(typeof config.checked == "boolean"){ // must be check menu item config?
34158                 config = new Roo.menu.CheckItem(config);
34159             }else{
34160                 config = new Roo.menu.Item(config);
34161             }
34162         }
34163         return this.addItem(config);
34164     },
34165
34166     /**
34167      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34168      * @param {String} text The text to display in the menu item
34169      * @return {Roo.menu.Item} The menu item that was added
34170      */
34171     addText : function(text){
34172         return this.addItem(new Roo.menu.TextItem({ text : text }));
34173     },
34174
34175     /**
34176      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34177      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34178      * @param {Roo.menu.Item} item The menu item to add
34179      * @return {Roo.menu.Item} The menu item that was added
34180      */
34181     insert : function(index, item){
34182         this.items.insert(index, item);
34183         if(this.ul){
34184             var li = document.createElement("li");
34185             li.className = "x-menu-list-item";
34186             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34187             item.render(li, this);
34188             this.delayAutoWidth();
34189         }
34190         return item;
34191     },
34192
34193     /**
34194      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34195      * @param {Roo.menu.Item} item The menu item to remove
34196      */
34197     remove : function(item){
34198         this.items.removeKey(item.id);
34199         item.destroy();
34200     },
34201
34202     /**
34203      * Removes and destroys all items in the menu
34204      */
34205     removeAll : function(){
34206         var f;
34207         while(f = this.items.first()){
34208             this.remove(f);
34209         }
34210     }
34211 });
34212
34213 // MenuNav is a private utility class used internally by the Menu
34214 Roo.menu.MenuNav = function(menu){
34215     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34216     this.scope = this.menu = menu;
34217 };
34218
34219 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34220     doRelay : function(e, h){
34221         var k = e.getKey();
34222         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34223             this.menu.tryActivate(0, 1);
34224             return false;
34225         }
34226         return h.call(this.scope || this, e, this.menu);
34227     },
34228
34229     up : function(e, m){
34230         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34231             m.tryActivate(m.items.length-1, -1);
34232         }
34233     },
34234
34235     down : function(e, m){
34236         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34237             m.tryActivate(0, 1);
34238         }
34239     },
34240
34241     right : function(e, m){
34242         if(m.activeItem){
34243             m.activeItem.expandMenu(true);
34244         }
34245     },
34246
34247     left : function(e, m){
34248         m.hide();
34249         if(m.parentMenu && m.parentMenu.activeItem){
34250             m.parentMenu.activeItem.activate();
34251         }
34252     },
34253
34254     enter : function(e, m){
34255         if(m.activeItem){
34256             e.stopPropagation();
34257             m.activeItem.onClick(e);
34258             m.fireEvent("click", this, m.activeItem);
34259             return true;
34260         }
34261     }
34262 });/*
34263  * Based on:
34264  * Ext JS Library 1.1.1
34265  * Copyright(c) 2006-2007, Ext JS, LLC.
34266  *
34267  * Originally Released Under LGPL - original licence link has changed is not relivant.
34268  *
34269  * Fork - LGPL
34270  * <script type="text/javascript">
34271  */
34272  
34273 /**
34274  * @class Roo.menu.MenuMgr
34275  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34276  * @singleton
34277  */
34278 Roo.menu.MenuMgr = function(){
34279    var menus, active, groups = {}, attached = false, lastShow = new Date();
34280
34281    // private - called when first menu is created
34282    function init(){
34283        menus = {};
34284        active = new Roo.util.MixedCollection();
34285        Roo.get(document).addKeyListener(27, function(){
34286            if(active.length > 0){
34287                hideAll();
34288            }
34289        });
34290    }
34291
34292    // private
34293    function hideAll(){
34294        if(active && active.length > 0){
34295            var c = active.clone();
34296            c.each(function(m){
34297                m.hide();
34298            });
34299        }
34300    }
34301
34302    // private
34303    function onHide(m){
34304        active.remove(m);
34305        if(active.length < 1){
34306            Roo.get(document).un("mousedown", onMouseDown);
34307            attached = false;
34308        }
34309    }
34310
34311    // private
34312    function onShow(m){
34313        var last = active.last();
34314        lastShow = new Date();
34315        active.add(m);
34316        if(!attached){
34317            Roo.get(document).on("mousedown", onMouseDown);
34318            attached = true;
34319        }
34320        if(m.parentMenu){
34321           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34322           m.parentMenu.activeChild = m;
34323        }else if(last && last.isVisible()){
34324           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34325        }
34326    }
34327
34328    // private
34329    function onBeforeHide(m){
34330        if(m.activeChild){
34331            m.activeChild.hide();
34332        }
34333        if(m.autoHideTimer){
34334            clearTimeout(m.autoHideTimer);
34335            delete m.autoHideTimer;
34336        }
34337    }
34338
34339    // private
34340    function onBeforeShow(m){
34341        var pm = m.parentMenu;
34342        if(!pm && !m.allowOtherMenus){
34343            hideAll();
34344        }else if(pm && pm.activeChild && active != m){
34345            pm.activeChild.hide();
34346        }
34347    }
34348
34349    // private
34350    function onMouseDown(e){
34351        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34352            hideAll();
34353        }
34354    }
34355
34356    // private
34357    function onBeforeCheck(mi, state){
34358        if(state){
34359            var g = groups[mi.group];
34360            for(var i = 0, l = g.length; i < l; i++){
34361                if(g[i] != mi){
34362                    g[i].setChecked(false);
34363                }
34364            }
34365        }
34366    }
34367
34368    return {
34369
34370        /**
34371         * Hides all menus that are currently visible
34372         */
34373        hideAll : function(){
34374             hideAll();  
34375        },
34376
34377        // private
34378        register : function(menu){
34379            if(!menus){
34380                init();
34381            }
34382            menus[menu.id] = menu;
34383            menu.on("beforehide", onBeforeHide);
34384            menu.on("hide", onHide);
34385            menu.on("beforeshow", onBeforeShow);
34386            menu.on("show", onShow);
34387            var g = menu.group;
34388            if(g && menu.events["checkchange"]){
34389                if(!groups[g]){
34390                    groups[g] = [];
34391                }
34392                groups[g].push(menu);
34393                menu.on("checkchange", onCheck);
34394            }
34395        },
34396
34397         /**
34398          * Returns a {@link Roo.menu.Menu} object
34399          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34400          * be used to generate and return a new Menu instance.
34401          */
34402        get : function(menu){
34403            if(typeof menu == "string"){ // menu id
34404                return menus[menu];
34405            }else if(menu.events){  // menu instance
34406                return menu;
34407            }else if(typeof menu.length == 'number'){ // array of menu items?
34408                return new Roo.menu.Menu({items:menu});
34409            }else{ // otherwise, must be a config
34410                return new Roo.menu.Menu(menu);
34411            }
34412        },
34413
34414        // private
34415        unregister : function(menu){
34416            delete menus[menu.id];
34417            menu.un("beforehide", onBeforeHide);
34418            menu.un("hide", onHide);
34419            menu.un("beforeshow", onBeforeShow);
34420            menu.un("show", onShow);
34421            var g = menu.group;
34422            if(g && menu.events["checkchange"]){
34423                groups[g].remove(menu);
34424                menu.un("checkchange", onCheck);
34425            }
34426        },
34427
34428        // private
34429        registerCheckable : function(menuItem){
34430            var g = menuItem.group;
34431            if(g){
34432                if(!groups[g]){
34433                    groups[g] = [];
34434                }
34435                groups[g].push(menuItem);
34436                menuItem.on("beforecheckchange", onBeforeCheck);
34437            }
34438        },
34439
34440        // private
34441        unregisterCheckable : function(menuItem){
34442            var g = menuItem.group;
34443            if(g){
34444                groups[g].remove(menuItem);
34445                menuItem.un("beforecheckchange", onBeforeCheck);
34446            }
34447        }
34448    };
34449 }();/*
34450  * Based on:
34451  * Ext JS Library 1.1.1
34452  * Copyright(c) 2006-2007, Ext JS, LLC.
34453  *
34454  * Originally Released Under LGPL - original licence link has changed is not relivant.
34455  *
34456  * Fork - LGPL
34457  * <script type="text/javascript">
34458  */
34459  
34460
34461 /**
34462  * @class Roo.menu.BaseItem
34463  * @extends Roo.Component
34464  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34465  * management and base configuration options shared by all menu components.
34466  * @constructor
34467  * Creates a new BaseItem
34468  * @param {Object} config Configuration options
34469  */
34470 Roo.menu.BaseItem = function(config){
34471     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34472
34473     this.addEvents({
34474         /**
34475          * @event click
34476          * Fires when this item is clicked
34477          * @param {Roo.menu.BaseItem} this
34478          * @param {Roo.EventObject} e
34479          */
34480         click: true,
34481         /**
34482          * @event activate
34483          * Fires when this item is activated
34484          * @param {Roo.menu.BaseItem} this
34485          */
34486         activate : true,
34487         /**
34488          * @event deactivate
34489          * Fires when this item is deactivated
34490          * @param {Roo.menu.BaseItem} this
34491          */
34492         deactivate : true
34493     });
34494
34495     if(this.handler){
34496         this.on("click", this.handler, this.scope, true);
34497     }
34498 };
34499
34500 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34501     /**
34502      * @cfg {Function} handler
34503      * A function that will handle the click event of this menu item (defaults to undefined)
34504      */
34505     /**
34506      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34507      */
34508     canActivate : false,
34509     /**
34510      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34511      */
34512     activeClass : "x-menu-item-active",
34513     /**
34514      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34515      */
34516     hideOnClick : true,
34517     /**
34518      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34519      */
34520     hideDelay : 100,
34521
34522     // private
34523     ctype: "Roo.menu.BaseItem",
34524
34525     // private
34526     actionMode : "container",
34527
34528     // private
34529     render : function(container, parentMenu){
34530         this.parentMenu = parentMenu;
34531         Roo.menu.BaseItem.superclass.render.call(this, container);
34532         this.container.menuItemId = this.id;
34533     },
34534
34535     // private
34536     onRender : function(container, position){
34537         this.el = Roo.get(this.el);
34538         container.dom.appendChild(this.el.dom);
34539     },
34540
34541     // private
34542     onClick : function(e){
34543         if(!this.disabled && this.fireEvent("click", this, e) !== false
34544                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34545             this.handleClick(e);
34546         }else{
34547             e.stopEvent();
34548         }
34549     },
34550
34551     // private
34552     activate : function(){
34553         if(this.disabled){
34554             return false;
34555         }
34556         var li = this.container;
34557         li.addClass(this.activeClass);
34558         this.region = li.getRegion().adjust(2, 2, -2, -2);
34559         this.fireEvent("activate", this);
34560         return true;
34561     },
34562
34563     // private
34564     deactivate : function(){
34565         this.container.removeClass(this.activeClass);
34566         this.fireEvent("deactivate", this);
34567     },
34568
34569     // private
34570     shouldDeactivate : function(e){
34571         return !this.region || !this.region.contains(e.getPoint());
34572     },
34573
34574     // private
34575     handleClick : function(e){
34576         if(this.hideOnClick){
34577             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34578         }
34579     },
34580
34581     // private
34582     expandMenu : function(autoActivate){
34583         // do nothing
34584     },
34585
34586     // private
34587     hideMenu : function(){
34588         // do nothing
34589     }
34590 });/*
34591  * Based on:
34592  * Ext JS Library 1.1.1
34593  * Copyright(c) 2006-2007, Ext JS, LLC.
34594  *
34595  * Originally Released Under LGPL - original licence link has changed is not relivant.
34596  *
34597  * Fork - LGPL
34598  * <script type="text/javascript">
34599  */
34600  
34601 /**
34602  * @class Roo.menu.Adapter
34603  * @extends Roo.menu.BaseItem
34604  * 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.
34605  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34606  * @constructor
34607  * Creates a new Adapter
34608  * @param {Object} config Configuration options
34609  */
34610 Roo.menu.Adapter = function(component, config){
34611     Roo.menu.Adapter.superclass.constructor.call(this, config);
34612     this.component = component;
34613 };
34614 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34615     // private
34616     canActivate : true,
34617
34618     // private
34619     onRender : function(container, position){
34620         this.component.render(container);
34621         this.el = this.component.getEl();
34622     },
34623
34624     // private
34625     activate : function(){
34626         if(this.disabled){
34627             return false;
34628         }
34629         this.component.focus();
34630         this.fireEvent("activate", this);
34631         return true;
34632     },
34633
34634     // private
34635     deactivate : function(){
34636         this.fireEvent("deactivate", this);
34637     },
34638
34639     // private
34640     disable : function(){
34641         this.component.disable();
34642         Roo.menu.Adapter.superclass.disable.call(this);
34643     },
34644
34645     // private
34646     enable : function(){
34647         this.component.enable();
34648         Roo.menu.Adapter.superclass.enable.call(this);
34649     }
34650 });/*
34651  * Based on:
34652  * Ext JS Library 1.1.1
34653  * Copyright(c) 2006-2007, Ext JS, LLC.
34654  *
34655  * Originally Released Under LGPL - original licence link has changed is not relivant.
34656  *
34657  * Fork - LGPL
34658  * <script type="text/javascript">
34659  */
34660
34661 /**
34662  * @class Roo.menu.TextItem
34663  * @extends Roo.menu.BaseItem
34664  * Adds a static text string to a menu, usually used as either a heading or group separator.
34665  * Note: old style constructor with text is still supported.
34666  * 
34667  * @constructor
34668  * Creates a new TextItem
34669  * @param {Object} cfg Configuration
34670  */
34671 Roo.menu.TextItem = function(cfg){
34672     if (typeof(cfg) == 'string') {
34673         this.text = cfg;
34674     } else {
34675         Roo.apply(this,cfg);
34676     }
34677     
34678     Roo.menu.TextItem.superclass.constructor.call(this);
34679 };
34680
34681 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34682     /**
34683      * @cfg {Boolean} text Text to show on item.
34684      */
34685     text : '',
34686     
34687     /**
34688      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34689      */
34690     hideOnClick : false,
34691     /**
34692      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34693      */
34694     itemCls : "x-menu-text",
34695
34696     // private
34697     onRender : function(){
34698         var s = document.createElement("span");
34699         s.className = this.itemCls;
34700         s.innerHTML = this.text;
34701         this.el = s;
34702         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34703     }
34704 });/*
34705  * Based on:
34706  * Ext JS Library 1.1.1
34707  * Copyright(c) 2006-2007, Ext JS, LLC.
34708  *
34709  * Originally Released Under LGPL - original licence link has changed is not relivant.
34710  *
34711  * Fork - LGPL
34712  * <script type="text/javascript">
34713  */
34714
34715 /**
34716  * @class Roo.menu.Separator
34717  * @extends Roo.menu.BaseItem
34718  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34719  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34720  * @constructor
34721  * @param {Object} config Configuration options
34722  */
34723 Roo.menu.Separator = function(config){
34724     Roo.menu.Separator.superclass.constructor.call(this, config);
34725 };
34726
34727 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34728     /**
34729      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34730      */
34731     itemCls : "x-menu-sep",
34732     /**
34733      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34734      */
34735     hideOnClick : false,
34736
34737     // private
34738     onRender : function(li){
34739         var s = document.createElement("span");
34740         s.className = this.itemCls;
34741         s.innerHTML = "&#160;";
34742         this.el = s;
34743         li.addClass("x-menu-sep-li");
34744         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34745     }
34746 });/*
34747  * Based on:
34748  * Ext JS Library 1.1.1
34749  * Copyright(c) 2006-2007, Ext JS, LLC.
34750  *
34751  * Originally Released Under LGPL - original licence link has changed is not relivant.
34752  *
34753  * Fork - LGPL
34754  * <script type="text/javascript">
34755  */
34756 /**
34757  * @class Roo.menu.Item
34758  * @extends Roo.menu.BaseItem
34759  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34760  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34761  * activation and click handling.
34762  * @constructor
34763  * Creates a new Item
34764  * @param {Object} config Configuration options
34765  */
34766 Roo.menu.Item = function(config){
34767     Roo.menu.Item.superclass.constructor.call(this, config);
34768     if(this.menu){
34769         this.menu = Roo.menu.MenuMgr.get(this.menu);
34770     }
34771 };
34772 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34773     
34774     /**
34775      * @cfg {String} text
34776      * The text to show on the menu item.
34777      */
34778     text: '',
34779      /**
34780      * @cfg {String} HTML to render in menu
34781      * The text to show on the menu item (HTML version).
34782      */
34783     html: '',
34784     /**
34785      * @cfg {String} icon
34786      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34787      */
34788     icon: undefined,
34789     /**
34790      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34791      */
34792     itemCls : "x-menu-item",
34793     /**
34794      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34795      */
34796     canActivate : true,
34797     /**
34798      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34799      */
34800     showDelay: 200,
34801     // doc'd in BaseItem
34802     hideDelay: 200,
34803
34804     // private
34805     ctype: "Roo.menu.Item",
34806     
34807     // private
34808     onRender : function(container, position){
34809         var el = document.createElement("a");
34810         el.hideFocus = true;
34811         el.unselectable = "on";
34812         el.href = this.href || "#";
34813         if(this.hrefTarget){
34814             el.target = this.hrefTarget;
34815         }
34816         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34817         
34818         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34819         
34820         el.innerHTML = String.format(
34821                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34822                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34823         this.el = el;
34824         Roo.menu.Item.superclass.onRender.call(this, container, position);
34825     },
34826
34827     /**
34828      * Sets the text to display in this menu item
34829      * @param {String} text The text to display
34830      * @param {Boolean} isHTML true to indicate text is pure html.
34831      */
34832     setText : function(text, isHTML){
34833         if (isHTML) {
34834             this.html = text;
34835         } else {
34836             this.text = text;
34837             this.html = '';
34838         }
34839         if(this.rendered){
34840             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34841      
34842             this.el.update(String.format(
34843                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34844                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34845             this.parentMenu.autoWidth();
34846         }
34847     },
34848
34849     // private
34850     handleClick : function(e){
34851         if(!this.href){ // if no link defined, stop the event automatically
34852             e.stopEvent();
34853         }
34854         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34855     },
34856
34857     // private
34858     activate : function(autoExpand){
34859         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34860             this.focus();
34861             if(autoExpand){
34862                 this.expandMenu();
34863             }
34864         }
34865         return true;
34866     },
34867
34868     // private
34869     shouldDeactivate : function(e){
34870         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34871             if(this.menu && this.menu.isVisible()){
34872                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34873             }
34874             return true;
34875         }
34876         return false;
34877     },
34878
34879     // private
34880     deactivate : function(){
34881         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34882         this.hideMenu();
34883     },
34884
34885     // private
34886     expandMenu : function(autoActivate){
34887         if(!this.disabled && this.menu){
34888             clearTimeout(this.hideTimer);
34889             delete this.hideTimer;
34890             if(!this.menu.isVisible() && !this.showTimer){
34891                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34892             }else if (this.menu.isVisible() && autoActivate){
34893                 this.menu.tryActivate(0, 1);
34894             }
34895         }
34896     },
34897
34898     // private
34899     deferExpand : function(autoActivate){
34900         delete this.showTimer;
34901         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34902         if(autoActivate){
34903             this.menu.tryActivate(0, 1);
34904         }
34905     },
34906
34907     // private
34908     hideMenu : function(){
34909         clearTimeout(this.showTimer);
34910         delete this.showTimer;
34911         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34912             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34913         }
34914     },
34915
34916     // private
34917     deferHide : function(){
34918         delete this.hideTimer;
34919         this.menu.hide();
34920     }
34921 });/*
34922  * Based on:
34923  * Ext JS Library 1.1.1
34924  * Copyright(c) 2006-2007, Ext JS, LLC.
34925  *
34926  * Originally Released Under LGPL - original licence link has changed is not relivant.
34927  *
34928  * Fork - LGPL
34929  * <script type="text/javascript">
34930  */
34931  
34932 /**
34933  * @class Roo.menu.CheckItem
34934  * @extends Roo.menu.Item
34935  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34936  * @constructor
34937  * Creates a new CheckItem
34938  * @param {Object} config Configuration options
34939  */
34940 Roo.menu.CheckItem = function(config){
34941     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34942     this.addEvents({
34943         /**
34944          * @event beforecheckchange
34945          * Fires before the checked value is set, providing an opportunity to cancel if needed
34946          * @param {Roo.menu.CheckItem} this
34947          * @param {Boolean} checked The new checked value that will be set
34948          */
34949         "beforecheckchange" : true,
34950         /**
34951          * @event checkchange
34952          * Fires after the checked value has been set
34953          * @param {Roo.menu.CheckItem} this
34954          * @param {Boolean} checked The checked value that was set
34955          */
34956         "checkchange" : true
34957     });
34958     if(this.checkHandler){
34959         this.on('checkchange', this.checkHandler, this.scope);
34960     }
34961 };
34962 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34963     /**
34964      * @cfg {String} group
34965      * All check items with the same group name will automatically be grouped into a single-select
34966      * radio button group (defaults to '')
34967      */
34968     /**
34969      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34970      */
34971     itemCls : "x-menu-item x-menu-check-item",
34972     /**
34973      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34974      */
34975     groupClass : "x-menu-group-item",
34976
34977     /**
34978      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34979      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34980      * initialized with checked = true will be rendered as checked.
34981      */
34982     checked: false,
34983
34984     // private
34985     ctype: "Roo.menu.CheckItem",
34986
34987     // private
34988     onRender : function(c){
34989         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34990         if(this.group){
34991             this.el.addClass(this.groupClass);
34992         }
34993         Roo.menu.MenuMgr.registerCheckable(this);
34994         if(this.checked){
34995             this.checked = false;
34996             this.setChecked(true, true);
34997         }
34998     },
34999
35000     // private
35001     destroy : function(){
35002         if(this.rendered){
35003             Roo.menu.MenuMgr.unregisterCheckable(this);
35004         }
35005         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35006     },
35007
35008     /**
35009      * Set the checked state of this item
35010      * @param {Boolean} checked The new checked value
35011      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35012      */
35013     setChecked : function(state, suppressEvent){
35014         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35015             if(this.container){
35016                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35017             }
35018             this.checked = state;
35019             if(suppressEvent !== true){
35020                 this.fireEvent("checkchange", this, state);
35021             }
35022         }
35023     },
35024
35025     // private
35026     handleClick : function(e){
35027        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35028            this.setChecked(!this.checked);
35029        }
35030        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35031     }
35032 });/*
35033  * Based on:
35034  * Ext JS Library 1.1.1
35035  * Copyright(c) 2006-2007, Ext JS, LLC.
35036  *
35037  * Originally Released Under LGPL - original licence link has changed is not relivant.
35038  *
35039  * Fork - LGPL
35040  * <script type="text/javascript">
35041  */
35042  
35043 /**
35044  * @class Roo.menu.DateItem
35045  * @extends Roo.menu.Adapter
35046  * A menu item that wraps the {@link Roo.DatPicker} component.
35047  * @constructor
35048  * Creates a new DateItem
35049  * @param {Object} config Configuration options
35050  */
35051 Roo.menu.DateItem = function(config){
35052     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35053     /** The Roo.DatePicker object @type Roo.DatePicker */
35054     this.picker = this.component;
35055     this.addEvents({select: true});
35056     
35057     this.picker.on("render", function(picker){
35058         picker.getEl().swallowEvent("click");
35059         picker.container.addClass("x-menu-date-item");
35060     });
35061
35062     this.picker.on("select", this.onSelect, this);
35063 };
35064
35065 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35066     // private
35067     onSelect : function(picker, date){
35068         this.fireEvent("select", this, date, picker);
35069         Roo.menu.DateItem.superclass.handleClick.call(this);
35070     }
35071 });/*
35072  * Based on:
35073  * Ext JS Library 1.1.1
35074  * Copyright(c) 2006-2007, Ext JS, LLC.
35075  *
35076  * Originally Released Under LGPL - original licence link has changed is not relivant.
35077  *
35078  * Fork - LGPL
35079  * <script type="text/javascript">
35080  */
35081  
35082 /**
35083  * @class Roo.menu.ColorItem
35084  * @extends Roo.menu.Adapter
35085  * A menu item that wraps the {@link Roo.ColorPalette} component.
35086  * @constructor
35087  * Creates a new ColorItem
35088  * @param {Object} config Configuration options
35089  */
35090 Roo.menu.ColorItem = function(config){
35091     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35092     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35093     this.palette = this.component;
35094     this.relayEvents(this.palette, ["select"]);
35095     if(this.selectHandler){
35096         this.on('select', this.selectHandler, this.scope);
35097     }
35098 };
35099 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35100  * Based on:
35101  * Ext JS Library 1.1.1
35102  * Copyright(c) 2006-2007, Ext JS, LLC.
35103  *
35104  * Originally Released Under LGPL - original licence link has changed is not relivant.
35105  *
35106  * Fork - LGPL
35107  * <script type="text/javascript">
35108  */
35109  
35110
35111 /**
35112  * @class Roo.menu.DateMenu
35113  * @extends Roo.menu.Menu
35114  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35115  * @constructor
35116  * Creates a new DateMenu
35117  * @param {Object} config Configuration options
35118  */
35119 Roo.menu.DateMenu = function(config){
35120     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35121     this.plain = true;
35122     var di = new Roo.menu.DateItem(config);
35123     this.add(di);
35124     /**
35125      * The {@link Roo.DatePicker} instance for this DateMenu
35126      * @type DatePicker
35127      */
35128     this.picker = di.picker;
35129     /**
35130      * @event select
35131      * @param {DatePicker} picker
35132      * @param {Date} date
35133      */
35134     this.relayEvents(di, ["select"]);
35135
35136     this.on('beforeshow', function(){
35137         if(this.picker){
35138             this.picker.hideMonthPicker(true);
35139         }
35140     }, this);
35141 };
35142 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35143     cls:'x-date-menu'
35144 });/*
35145  * Based on:
35146  * Ext JS Library 1.1.1
35147  * Copyright(c) 2006-2007, Ext JS, LLC.
35148  *
35149  * Originally Released Under LGPL - original licence link has changed is not relivant.
35150  *
35151  * Fork - LGPL
35152  * <script type="text/javascript">
35153  */
35154  
35155
35156 /**
35157  * @class Roo.menu.ColorMenu
35158  * @extends Roo.menu.Menu
35159  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35160  * @constructor
35161  * Creates a new ColorMenu
35162  * @param {Object} config Configuration options
35163  */
35164 Roo.menu.ColorMenu = function(config){
35165     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35166     this.plain = true;
35167     var ci = new Roo.menu.ColorItem(config);
35168     this.add(ci);
35169     /**
35170      * The {@link Roo.ColorPalette} instance for this ColorMenu
35171      * @type ColorPalette
35172      */
35173     this.palette = ci.palette;
35174     /**
35175      * @event select
35176      * @param {ColorPalette} palette
35177      * @param {String} color
35178      */
35179     this.relayEvents(ci, ["select"]);
35180 };
35181 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35182  * Based on:
35183  * Ext JS Library 1.1.1
35184  * Copyright(c) 2006-2007, Ext JS, LLC.
35185  *
35186  * Originally Released Under LGPL - original licence link has changed is not relivant.
35187  *
35188  * Fork - LGPL
35189  * <script type="text/javascript">
35190  */
35191  
35192 /**
35193  * @class Roo.form.Field
35194  * @extends Roo.BoxComponent
35195  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35196  * @constructor
35197  * Creates a new Field
35198  * @param {Object} config Configuration options
35199  */
35200 Roo.form.Field = function(config){
35201     Roo.form.Field.superclass.constructor.call(this, config);
35202 };
35203
35204 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35205     /**
35206      * @cfg {String} fieldLabel Label to use when rendering a form.
35207      */
35208        /**
35209      * @cfg {String} qtip Mouse over tip
35210      */
35211      
35212     /**
35213      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35214      */
35215     invalidClass : "x-form-invalid",
35216     /**
35217      * @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")
35218      */
35219     invalidText : "The value in this field is invalid",
35220     /**
35221      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35222      */
35223     focusClass : "x-form-focus",
35224     /**
35225      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35226       automatic validation (defaults to "keyup").
35227      */
35228     validationEvent : "keyup",
35229     /**
35230      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35231      */
35232     validateOnBlur : true,
35233     /**
35234      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35235      */
35236     validationDelay : 250,
35237     /**
35238      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35239      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35240      */
35241     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35242     /**
35243      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35244      */
35245     fieldClass : "x-form-field",
35246     /**
35247      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35248      *<pre>
35249 Value         Description
35250 -----------   ----------------------------------------------------------------------
35251 qtip          Display a quick tip when the user hovers over the field
35252 title         Display a default browser title attribute popup
35253 under         Add a block div beneath the field containing the error text
35254 side          Add an error icon to the right of the field with a popup on hover
35255 [element id]  Add the error text directly to the innerHTML of the specified element
35256 </pre>
35257      */
35258     msgTarget : 'qtip',
35259     /**
35260      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35261      */
35262     msgFx : 'normal',
35263
35264     /**
35265      * @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.
35266      */
35267     readOnly : false,
35268
35269     /**
35270      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35271      */
35272     disabled : false,
35273
35274     /**
35275      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35276      */
35277     inputType : undefined,
35278     
35279     /**
35280      * @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).
35281          */
35282         tabIndex : undefined,
35283         
35284     // private
35285     isFormField : true,
35286
35287     // private
35288     hasFocus : false,
35289     /**
35290      * @property {Roo.Element} fieldEl
35291      * Element Containing the rendered Field (with label etc.)
35292      */
35293     /**
35294      * @cfg {Mixed} value A value to initialize this field with.
35295      */
35296     value : undefined,
35297
35298     /**
35299      * @cfg {String} name The field's HTML name attribute.
35300      */
35301     /**
35302      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35303      */
35304
35305         // private ??
35306         initComponent : function(){
35307         Roo.form.Field.superclass.initComponent.call(this);
35308         this.addEvents({
35309             /**
35310              * @event focus
35311              * Fires when this field receives input focus.
35312              * @param {Roo.form.Field} this
35313              */
35314             focus : true,
35315             /**
35316              * @event blur
35317              * Fires when this field loses input focus.
35318              * @param {Roo.form.Field} this
35319              */
35320             blur : true,
35321             /**
35322              * @event specialkey
35323              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35324              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35325              * @param {Roo.form.Field} this
35326              * @param {Roo.EventObject} e The event object
35327              */
35328             specialkey : true,
35329             /**
35330              * @event change
35331              * Fires just before the field blurs if the field value has changed.
35332              * @param {Roo.form.Field} this
35333              * @param {Mixed} newValue The new value
35334              * @param {Mixed} oldValue The original value
35335              */
35336             change : true,
35337             /**
35338              * @event invalid
35339              * Fires after the field has been marked as invalid.
35340              * @param {Roo.form.Field} this
35341              * @param {String} msg The validation message
35342              */
35343             invalid : true,
35344             /**
35345              * @event valid
35346              * Fires after the field has been validated with no errors.
35347              * @param {Roo.form.Field} this
35348              */
35349             valid : true,
35350              /**
35351              * @event keyup
35352              * Fires after the key up
35353              * @param {Roo.form.Field} this
35354              * @param {Roo.EventObject}  e The event Object
35355              */
35356             keyup : true
35357         });
35358     },
35359
35360     /**
35361      * Returns the name attribute of the field if available
35362      * @return {String} name The field name
35363      */
35364     getName: function(){
35365          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35366     },
35367
35368     // private
35369     onRender : function(ct, position){
35370         Roo.form.Field.superclass.onRender.call(this, ct, position);
35371         if(!this.el){
35372             var cfg = this.getAutoCreate();
35373             if(!cfg.name){
35374                 cfg.name = this.name || this.id;
35375             }
35376             if(this.inputType){
35377                 cfg.type = this.inputType;
35378             }
35379             this.el = ct.createChild(cfg, position);
35380         }
35381         var type = this.el.dom.type;
35382         if(type){
35383             if(type == 'password'){
35384                 type = 'text';
35385             }
35386             this.el.addClass('x-form-'+type);
35387         }
35388         if(this.readOnly){
35389             this.el.dom.readOnly = true;
35390         }
35391         if(this.tabIndex !== undefined){
35392             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35393         }
35394
35395         this.el.addClass([this.fieldClass, this.cls]);
35396         this.initValue();
35397     },
35398
35399     /**
35400      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35401      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35402      * @return {Roo.form.Field} this
35403      */
35404     applyTo : function(target){
35405         this.allowDomMove = false;
35406         this.el = Roo.get(target);
35407         this.render(this.el.dom.parentNode);
35408         return this;
35409     },
35410
35411     // private
35412     initValue : function(){
35413         if(this.value !== undefined){
35414             this.setValue(this.value);
35415         }else if(this.el.dom.value.length > 0){
35416             this.setValue(this.el.dom.value);
35417         }
35418     },
35419
35420     /**
35421      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35422      */
35423     isDirty : function() {
35424         if(this.disabled) {
35425             return false;
35426         }
35427         return String(this.getValue()) !== String(this.originalValue);
35428     },
35429
35430     // private
35431     afterRender : function(){
35432         Roo.form.Field.superclass.afterRender.call(this);
35433         this.initEvents();
35434     },
35435
35436     // private
35437     fireKey : function(e){
35438         //Roo.log('field ' + e.getKey());
35439         if(e.isNavKeyPress()){
35440             this.fireEvent("specialkey", this, e);
35441         }
35442     },
35443
35444     /**
35445      * Resets the current field value to the originally loaded value and clears any validation messages
35446      */
35447     reset : function(){
35448         this.setValue(this.originalValue);
35449         this.clearInvalid();
35450     },
35451
35452     // private
35453     initEvents : function(){
35454         // safari killled keypress - so keydown is now used..
35455         this.el.on("keydown" , this.fireKey,  this);
35456         this.el.on("focus", this.onFocus,  this);
35457         this.el.on("blur", this.onBlur,  this);
35458         this.el.relayEvent('keyup', this);
35459
35460         // reference to original value for reset
35461         this.originalValue = this.getValue();
35462     },
35463
35464     // private
35465     onFocus : function(){
35466         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35467             this.el.addClass(this.focusClass);
35468         }
35469         if(!this.hasFocus){
35470             this.hasFocus = true;
35471             this.startValue = this.getValue();
35472             this.fireEvent("focus", this);
35473         }
35474     },
35475
35476     beforeBlur : Roo.emptyFn,
35477
35478     // private
35479     onBlur : function(){
35480         this.beforeBlur();
35481         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35482             this.el.removeClass(this.focusClass);
35483         }
35484         this.hasFocus = false;
35485         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35486             this.validate();
35487         }
35488         var v = this.getValue();
35489         if(String(v) !== String(this.startValue)){
35490             this.fireEvent('change', this, v, this.startValue);
35491         }
35492         this.fireEvent("blur", this);
35493     },
35494
35495     /**
35496      * Returns whether or not the field value is currently valid
35497      * @param {Boolean} preventMark True to disable marking the field invalid
35498      * @return {Boolean} True if the value is valid, else false
35499      */
35500     isValid : function(preventMark){
35501         if(this.disabled){
35502             return true;
35503         }
35504         var restore = this.preventMark;
35505         this.preventMark = preventMark === true;
35506         var v = this.validateValue(this.processValue(this.getRawValue()));
35507         this.preventMark = restore;
35508         return v;
35509     },
35510
35511     /**
35512      * Validates the field value
35513      * @return {Boolean} True if the value is valid, else false
35514      */
35515     validate : function(){
35516         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35517             this.clearInvalid();
35518             return true;
35519         }
35520         return false;
35521     },
35522
35523     processValue : function(value){
35524         return value;
35525     },
35526
35527     // private
35528     // Subclasses should provide the validation implementation by overriding this
35529     validateValue : function(value){
35530         return true;
35531     },
35532
35533     /**
35534      * Mark this field as invalid
35535      * @param {String} msg The validation message
35536      */
35537     markInvalid : function(msg){
35538         if(!this.rendered || this.preventMark){ // not rendered
35539             return;
35540         }
35541         this.el.addClass(this.invalidClass);
35542         msg = msg || this.invalidText;
35543         switch(this.msgTarget){
35544             case 'qtip':
35545                 this.el.dom.qtip = msg;
35546                 this.el.dom.qclass = 'x-form-invalid-tip';
35547                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35548                     Roo.QuickTips.enable();
35549                 }
35550                 break;
35551             case 'title':
35552                 this.el.dom.title = msg;
35553                 break;
35554             case 'under':
35555                 if(!this.errorEl){
35556                     var elp = this.el.findParent('.x-form-element', 5, true);
35557                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35558                     this.errorEl.setWidth(elp.getWidth(true)-20);
35559                 }
35560                 this.errorEl.update(msg);
35561                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35562                 break;
35563             case 'side':
35564                 if(!this.errorIcon){
35565                     var elp = this.el.findParent('.x-form-element', 5, true);
35566                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35567                 }
35568                 this.alignErrorIcon();
35569                 this.errorIcon.dom.qtip = msg;
35570                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35571                 this.errorIcon.show();
35572                 this.on('resize', this.alignErrorIcon, this);
35573                 break;
35574             default:
35575                 var t = Roo.getDom(this.msgTarget);
35576                 t.innerHTML = msg;
35577                 t.style.display = this.msgDisplay;
35578                 break;
35579         }
35580         this.fireEvent('invalid', this, msg);
35581     },
35582
35583     // private
35584     alignErrorIcon : function(){
35585         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35586     },
35587
35588     /**
35589      * Clear any invalid styles/messages for this field
35590      */
35591     clearInvalid : function(){
35592         if(!this.rendered || this.preventMark){ // not rendered
35593             return;
35594         }
35595         this.el.removeClass(this.invalidClass);
35596         switch(this.msgTarget){
35597             case 'qtip':
35598                 this.el.dom.qtip = '';
35599                 break;
35600             case 'title':
35601                 this.el.dom.title = '';
35602                 break;
35603             case 'under':
35604                 if(this.errorEl){
35605                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35606                 }
35607                 break;
35608             case 'side':
35609                 if(this.errorIcon){
35610                     this.errorIcon.dom.qtip = '';
35611                     this.errorIcon.hide();
35612                     this.un('resize', this.alignErrorIcon, this);
35613                 }
35614                 break;
35615             default:
35616                 var t = Roo.getDom(this.msgTarget);
35617                 t.innerHTML = '';
35618                 t.style.display = 'none';
35619                 break;
35620         }
35621         this.fireEvent('valid', this);
35622     },
35623
35624     /**
35625      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35626      * @return {Mixed} value The field value
35627      */
35628     getRawValue : function(){
35629         var v = this.el.getValue();
35630         if(v === this.emptyText){
35631             v = '';
35632         }
35633         return v;
35634     },
35635
35636     /**
35637      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35638      * @return {Mixed} value The field value
35639      */
35640     getValue : function(){
35641         var v = this.el.getValue();
35642         if(v === this.emptyText || v === undefined){
35643             v = '';
35644         }
35645         return v;
35646     },
35647
35648     /**
35649      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35650      * @param {Mixed} value The value to set
35651      */
35652     setRawValue : function(v){
35653         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35654     },
35655
35656     /**
35657      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35658      * @param {Mixed} value The value to set
35659      */
35660     setValue : function(v){
35661         this.value = v;
35662         if(this.rendered){
35663             this.el.dom.value = (v === null || v === undefined ? '' : v);
35664             this.validate();
35665         }
35666     },
35667
35668     adjustSize : function(w, h){
35669         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35670         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35671         return s;
35672     },
35673
35674     adjustWidth : function(tag, w){
35675         tag = tag.toLowerCase();
35676         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35677             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35678                 if(tag == 'input'){
35679                     return w + 2;
35680                 }
35681                 if(tag = 'textarea'){
35682                     return w-2;
35683                 }
35684             }else if(Roo.isOpera){
35685                 if(tag == 'input'){
35686                     return w + 2;
35687                 }
35688                 if(tag = 'textarea'){
35689                     return w-2;
35690                 }
35691             }
35692         }
35693         return w;
35694     }
35695 });
35696
35697
35698 // anything other than normal should be considered experimental
35699 Roo.form.Field.msgFx = {
35700     normal : {
35701         show: function(msgEl, f){
35702             msgEl.setDisplayed('block');
35703         },
35704
35705         hide : function(msgEl, f){
35706             msgEl.setDisplayed(false).update('');
35707         }
35708     },
35709
35710     slide : {
35711         show: function(msgEl, f){
35712             msgEl.slideIn('t', {stopFx:true});
35713         },
35714
35715         hide : function(msgEl, f){
35716             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35717         }
35718     },
35719
35720     slideRight : {
35721         show: function(msgEl, f){
35722             msgEl.fixDisplay();
35723             msgEl.alignTo(f.el, 'tl-tr');
35724             msgEl.slideIn('l', {stopFx:true});
35725         },
35726
35727         hide : function(msgEl, f){
35728             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35729         }
35730     }
35731 };/*
35732  * Based on:
35733  * Ext JS Library 1.1.1
35734  * Copyright(c) 2006-2007, Ext JS, LLC.
35735  *
35736  * Originally Released Under LGPL - original licence link has changed is not relivant.
35737  *
35738  * Fork - LGPL
35739  * <script type="text/javascript">
35740  */
35741  
35742
35743 /**
35744  * @class Roo.form.TextField
35745  * @extends Roo.form.Field
35746  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35747  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35748  * @constructor
35749  * Creates a new TextField
35750  * @param {Object} config Configuration options
35751  */
35752 Roo.form.TextField = function(config){
35753     Roo.form.TextField.superclass.constructor.call(this, config);
35754     this.addEvents({
35755         /**
35756          * @event autosize
35757          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35758          * according to the default logic, but this event provides a hook for the developer to apply additional
35759          * logic at runtime to resize the field if needed.
35760              * @param {Roo.form.Field} this This text field
35761              * @param {Number} width The new field width
35762              */
35763         autosize : true
35764     });
35765 };
35766
35767 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35768     /**
35769      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35770      */
35771     grow : false,
35772     /**
35773      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35774      */
35775     growMin : 30,
35776     /**
35777      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35778      */
35779     growMax : 800,
35780     /**
35781      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35782      */
35783     vtype : null,
35784     /**
35785      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35786      */
35787     maskRe : null,
35788     /**
35789      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35790      */
35791     disableKeyFilter : false,
35792     /**
35793      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35794      */
35795     allowBlank : true,
35796     /**
35797      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35798      */
35799     minLength : 0,
35800     /**
35801      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35802      */
35803     maxLength : Number.MAX_VALUE,
35804     /**
35805      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35806      */
35807     minLengthText : "The minimum length for this field is {0}",
35808     /**
35809      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35810      */
35811     maxLengthText : "The maximum length for this field is {0}",
35812     /**
35813      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35814      */
35815     selectOnFocus : false,
35816     /**
35817      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35818      */
35819     blankText : "This field is required",
35820     /**
35821      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35822      * If available, this function will be called only after the basic validators all return true, and will be passed the
35823      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35824      */
35825     validator : null,
35826     /**
35827      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35828      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35829      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35830      */
35831     regex : null,
35832     /**
35833      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35834      */
35835     regexText : "",
35836     /**
35837      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35838      */
35839     emptyText : null,
35840     /**
35841      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35842      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35843      */
35844     emptyClass : 'x-form-empty-field',
35845
35846     // private
35847     initEvents : function(){
35848         Roo.form.TextField.superclass.initEvents.call(this);
35849         if(this.validationEvent == 'keyup'){
35850             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35851             this.el.on('keyup', this.filterValidation, this);
35852         }
35853         else if(this.validationEvent !== false){
35854             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35855         }
35856         if(this.selectOnFocus || this.emptyText){
35857             this.on("focus", this.preFocus, this);
35858             if(this.emptyText){
35859                 this.on('blur', this.postBlur, this);
35860                 this.applyEmptyText();
35861             }
35862         }
35863         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35864             this.el.on("keypress", this.filterKeys, this);
35865         }
35866         if(this.grow){
35867             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35868             this.el.on("click", this.autoSize,  this);
35869         }
35870     },
35871
35872     processValue : function(value){
35873         if(this.stripCharsRe){
35874             var newValue = value.replace(this.stripCharsRe, '');
35875             if(newValue !== value){
35876                 this.setRawValue(newValue);
35877                 return newValue;
35878             }
35879         }
35880         return value;
35881     },
35882
35883     filterValidation : function(e){
35884         if(!e.isNavKeyPress()){
35885             this.validationTask.delay(this.validationDelay);
35886         }
35887     },
35888
35889     // private
35890     onKeyUp : function(e){
35891         if(!e.isNavKeyPress()){
35892             this.autoSize();
35893         }
35894     },
35895
35896     /**
35897      * Resets the current field value to the originally-loaded value and clears any validation messages.
35898      * Also adds emptyText and emptyClass if the original value was blank.
35899      */
35900     reset : function(){
35901         Roo.form.TextField.superclass.reset.call(this);
35902         this.applyEmptyText();
35903     },
35904
35905     applyEmptyText : function(){
35906         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35907             this.setRawValue(this.emptyText);
35908             this.el.addClass(this.emptyClass);
35909         }
35910     },
35911
35912     // private
35913     preFocus : function(){
35914         if(this.emptyText){
35915             if(this.el.dom.value == this.emptyText){
35916                 this.setRawValue('');
35917             }
35918             this.el.removeClass(this.emptyClass);
35919         }
35920         if(this.selectOnFocus){
35921             this.el.dom.select();
35922         }
35923     },
35924
35925     // private
35926     postBlur : function(){
35927         this.applyEmptyText();
35928     },
35929
35930     // private
35931     filterKeys : function(e){
35932         var k = e.getKey();
35933         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35934             return;
35935         }
35936         var c = e.getCharCode(), cc = String.fromCharCode(c);
35937         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35938             return;
35939         }
35940         if(!this.maskRe.test(cc)){
35941             e.stopEvent();
35942         }
35943     },
35944
35945     setValue : function(v){
35946         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35947             this.el.removeClass(this.emptyClass);
35948         }
35949         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35950         this.applyEmptyText();
35951         this.autoSize();
35952     },
35953
35954     /**
35955      * Validates a value according to the field's validation rules and marks the field as invalid
35956      * if the validation fails
35957      * @param {Mixed} value The value to validate
35958      * @return {Boolean} True if the value is valid, else false
35959      */
35960     validateValue : function(value){
35961         if(value.length < 1 || value === this.emptyText){ // if it's blank
35962              if(this.allowBlank){
35963                 this.clearInvalid();
35964                 return true;
35965              }else{
35966                 this.markInvalid(this.blankText);
35967                 return false;
35968              }
35969         }
35970         if(value.length < this.minLength){
35971             this.markInvalid(String.format(this.minLengthText, this.minLength));
35972             return false;
35973         }
35974         if(value.length > this.maxLength){
35975             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35976             return false;
35977         }
35978         if(this.vtype){
35979             var vt = Roo.form.VTypes;
35980             if(!vt[this.vtype](value, this)){
35981                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35982                 return false;
35983             }
35984         }
35985         if(typeof this.validator == "function"){
35986             var msg = this.validator(value);
35987             if(msg !== true){
35988                 this.markInvalid(msg);
35989                 return false;
35990             }
35991         }
35992         if(this.regex && !this.regex.test(value)){
35993             this.markInvalid(this.regexText);
35994             return false;
35995         }
35996         return true;
35997     },
35998
35999     /**
36000      * Selects text in this field
36001      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36002      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36003      */
36004     selectText : function(start, end){
36005         var v = this.getRawValue();
36006         if(v.length > 0){
36007             start = start === undefined ? 0 : start;
36008             end = end === undefined ? v.length : end;
36009             var d = this.el.dom;
36010             if(d.setSelectionRange){
36011                 d.setSelectionRange(start, end);
36012             }else if(d.createTextRange){
36013                 var range = d.createTextRange();
36014                 range.moveStart("character", start);
36015                 range.moveEnd("character", v.length-end);
36016                 range.select();
36017             }
36018         }
36019     },
36020
36021     /**
36022      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36023      * This only takes effect if grow = true, and fires the autosize event.
36024      */
36025     autoSize : function(){
36026         if(!this.grow || !this.rendered){
36027             return;
36028         }
36029         if(!this.metrics){
36030             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36031         }
36032         var el = this.el;
36033         var v = el.dom.value;
36034         var d = document.createElement('div');
36035         d.appendChild(document.createTextNode(v));
36036         v = d.innerHTML;
36037         d = null;
36038         v += "&#160;";
36039         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36040         this.el.setWidth(w);
36041         this.fireEvent("autosize", this, w);
36042     }
36043 });/*
36044  * Based on:
36045  * Ext JS Library 1.1.1
36046  * Copyright(c) 2006-2007, Ext JS, LLC.
36047  *
36048  * Originally Released Under LGPL - original licence link has changed is not relivant.
36049  *
36050  * Fork - LGPL
36051  * <script type="text/javascript">
36052  */
36053  
36054 /**
36055  * @class Roo.form.Hidden
36056  * @extends Roo.form.TextField
36057  * Simple Hidden element used on forms 
36058  * 
36059  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36060  * 
36061  * @constructor
36062  * Creates a new Hidden form element.
36063  * @param {Object} config Configuration options
36064  */
36065
36066
36067
36068 // easy hidden field...
36069 Roo.form.Hidden = function(config){
36070     Roo.form.Hidden.superclass.constructor.call(this, config);
36071 };
36072   
36073 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36074     fieldLabel:      '',
36075     inputType:      'hidden',
36076     width:          50,
36077     allowBlank:     true,
36078     labelSeparator: '',
36079     hidden:         true,
36080     itemCls :       'x-form-item-display-none'
36081
36082
36083 });
36084
36085
36086 /*
36087  * Based on:
36088  * Ext JS Library 1.1.1
36089  * Copyright(c) 2006-2007, Ext JS, LLC.
36090  *
36091  * Originally Released Under LGPL - original licence link has changed is not relivant.
36092  *
36093  * Fork - LGPL
36094  * <script type="text/javascript">
36095  */
36096  
36097 /**
36098  * @class Roo.form.TriggerField
36099  * @extends Roo.form.TextField
36100  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36101  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36102  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36103  * for which you can provide a custom implementation.  For example:
36104  * <pre><code>
36105 var trigger = new Roo.form.TriggerField();
36106 trigger.onTriggerClick = myTriggerFn;
36107 trigger.applyTo('my-field');
36108 </code></pre>
36109  *
36110  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36111  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36112  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36113  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36114  * @constructor
36115  * Create a new TriggerField.
36116  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36117  * to the base TextField)
36118  */
36119 Roo.form.TriggerField = function(config){
36120     this.mimicing = false;
36121     Roo.form.TriggerField.superclass.constructor.call(this, config);
36122 };
36123
36124 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36125     /**
36126      * @cfg {String} triggerClass A CSS class to apply to the trigger
36127      */
36128     /**
36129      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36130      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36131      */
36132     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36133     /**
36134      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36135      */
36136     hideTrigger:false,
36137
36138     /** @cfg {Boolean} grow @hide */
36139     /** @cfg {Number} growMin @hide */
36140     /** @cfg {Number} growMax @hide */
36141
36142     /**
36143      * @hide 
36144      * @method
36145      */
36146     autoSize: Roo.emptyFn,
36147     // private
36148     monitorTab : true,
36149     // private
36150     deferHeight : true,
36151
36152     
36153     actionMode : 'wrap',
36154     // private
36155     onResize : function(w, h){
36156         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36157         if(typeof w == 'number'){
36158             var x = w - this.trigger.getWidth();
36159             this.el.setWidth(this.adjustWidth('input', x));
36160             this.trigger.setStyle('left', x+'px');
36161         }
36162     },
36163
36164     // private
36165     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36166
36167     // private
36168     getResizeEl : function(){
36169         return this.wrap;
36170     },
36171
36172     // private
36173     getPositionEl : function(){
36174         return this.wrap;
36175     },
36176
36177     // private
36178     alignErrorIcon : function(){
36179         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36180     },
36181
36182     // private
36183     onRender : function(ct, position){
36184         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36185         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36186         this.trigger = this.wrap.createChild(this.triggerConfig ||
36187                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36188         if(this.hideTrigger){
36189             this.trigger.setDisplayed(false);
36190         }
36191         this.initTrigger();
36192         if(!this.width){
36193             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36194         }
36195     },
36196
36197     // private
36198     initTrigger : function(){
36199         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36200         this.trigger.addClassOnOver('x-form-trigger-over');
36201         this.trigger.addClassOnClick('x-form-trigger-click');
36202     },
36203
36204     // private
36205     onDestroy : function(){
36206         if(this.trigger){
36207             this.trigger.removeAllListeners();
36208             this.trigger.remove();
36209         }
36210         if(this.wrap){
36211             this.wrap.remove();
36212         }
36213         Roo.form.TriggerField.superclass.onDestroy.call(this);
36214     },
36215
36216     // private
36217     onFocus : function(){
36218         Roo.form.TriggerField.superclass.onFocus.call(this);
36219         if(!this.mimicing){
36220             this.wrap.addClass('x-trigger-wrap-focus');
36221             this.mimicing = true;
36222             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36223             if(this.monitorTab){
36224                 this.el.on("keydown", this.checkTab, this);
36225             }
36226         }
36227     },
36228
36229     // private
36230     checkTab : function(e){
36231         if(e.getKey() == e.TAB){
36232             this.triggerBlur();
36233         }
36234     },
36235
36236     // private
36237     onBlur : function(){
36238         // do nothing
36239     },
36240
36241     // private
36242     mimicBlur : function(e, t){
36243         if(!this.wrap.contains(t) && this.validateBlur()){
36244             this.triggerBlur();
36245         }
36246     },
36247
36248     // private
36249     triggerBlur : function(){
36250         this.mimicing = false;
36251         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36252         if(this.monitorTab){
36253             this.el.un("keydown", this.checkTab, this);
36254         }
36255         this.wrap.removeClass('x-trigger-wrap-focus');
36256         Roo.form.TriggerField.superclass.onBlur.call(this);
36257     },
36258
36259     // private
36260     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36261     validateBlur : function(e, t){
36262         return true;
36263     },
36264
36265     // private
36266     onDisable : function(){
36267         Roo.form.TriggerField.superclass.onDisable.call(this);
36268         if(this.wrap){
36269             this.wrap.addClass('x-item-disabled');
36270         }
36271     },
36272
36273     // private
36274     onEnable : function(){
36275         Roo.form.TriggerField.superclass.onEnable.call(this);
36276         if(this.wrap){
36277             this.wrap.removeClass('x-item-disabled');
36278         }
36279     },
36280
36281     // private
36282     onShow : function(){
36283         var ae = this.getActionEl();
36284         
36285         if(ae){
36286             ae.dom.style.display = '';
36287             ae.dom.style.visibility = 'visible';
36288         }
36289     },
36290
36291     // private
36292     
36293     onHide : function(){
36294         var ae = this.getActionEl();
36295         ae.dom.style.display = 'none';
36296     },
36297
36298     /**
36299      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36300      * by an implementing function.
36301      * @method
36302      * @param {EventObject} e
36303      */
36304     onTriggerClick : Roo.emptyFn
36305 });
36306
36307 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36308 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36309 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36310 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36311     initComponent : function(){
36312         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36313
36314         this.triggerConfig = {
36315             tag:'span', cls:'x-form-twin-triggers', cn:[
36316             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36317             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36318         ]};
36319     },
36320
36321     getTrigger : function(index){
36322         return this.triggers[index];
36323     },
36324
36325     initTrigger : function(){
36326         var ts = this.trigger.select('.x-form-trigger', true);
36327         this.wrap.setStyle('overflow', 'hidden');
36328         var triggerField = this;
36329         ts.each(function(t, all, index){
36330             t.hide = function(){
36331                 var w = triggerField.wrap.getWidth();
36332                 this.dom.style.display = 'none';
36333                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36334             };
36335             t.show = function(){
36336                 var w = triggerField.wrap.getWidth();
36337                 this.dom.style.display = '';
36338                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36339             };
36340             var triggerIndex = 'Trigger'+(index+1);
36341
36342             if(this['hide'+triggerIndex]){
36343                 t.dom.style.display = 'none';
36344             }
36345             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36346             t.addClassOnOver('x-form-trigger-over');
36347             t.addClassOnClick('x-form-trigger-click');
36348         }, this);
36349         this.triggers = ts.elements;
36350     },
36351
36352     onTrigger1Click : Roo.emptyFn,
36353     onTrigger2Click : Roo.emptyFn
36354 });/*
36355  * Based on:
36356  * Ext JS Library 1.1.1
36357  * Copyright(c) 2006-2007, Ext JS, LLC.
36358  *
36359  * Originally Released Under LGPL - original licence link has changed is not relivant.
36360  *
36361  * Fork - LGPL
36362  * <script type="text/javascript">
36363  */
36364  
36365 /**
36366  * @class Roo.form.TextArea
36367  * @extends Roo.form.TextField
36368  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36369  * support for auto-sizing.
36370  * @constructor
36371  * Creates a new TextArea
36372  * @param {Object} config Configuration options
36373  */
36374 Roo.form.TextArea = function(config){
36375     Roo.form.TextArea.superclass.constructor.call(this, config);
36376     // these are provided exchanges for backwards compat
36377     // minHeight/maxHeight were replaced by growMin/growMax to be
36378     // compatible with TextField growing config values
36379     if(this.minHeight !== undefined){
36380         this.growMin = this.minHeight;
36381     }
36382     if(this.maxHeight !== undefined){
36383         this.growMax = this.maxHeight;
36384     }
36385 };
36386
36387 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36388     /**
36389      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36390      */
36391     growMin : 60,
36392     /**
36393      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36394      */
36395     growMax: 1000,
36396     /**
36397      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36398      * in the field (equivalent to setting overflow: hidden, defaults to false)
36399      */
36400     preventScrollbars: false,
36401     /**
36402      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36403      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36404      */
36405
36406     // private
36407     onRender : function(ct, position){
36408         if(!this.el){
36409             this.defaultAutoCreate = {
36410                 tag: "textarea",
36411                 style:"width:300px;height:60px;",
36412                 autocomplete: "off"
36413             };
36414         }
36415         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36416         if(this.grow){
36417             this.textSizeEl = Roo.DomHelper.append(document.body, {
36418                 tag: "pre", cls: "x-form-grow-sizer"
36419             });
36420             if(this.preventScrollbars){
36421                 this.el.setStyle("overflow", "hidden");
36422             }
36423             this.el.setHeight(this.growMin);
36424         }
36425     },
36426
36427     onDestroy : function(){
36428         if(this.textSizeEl){
36429             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36430         }
36431         Roo.form.TextArea.superclass.onDestroy.call(this);
36432     },
36433
36434     // private
36435     onKeyUp : function(e){
36436         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36437             this.autoSize();
36438         }
36439     },
36440
36441     /**
36442      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36443      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36444      */
36445     autoSize : function(){
36446         if(!this.grow || !this.textSizeEl){
36447             return;
36448         }
36449         var el = this.el;
36450         var v = el.dom.value;
36451         var ts = this.textSizeEl;
36452
36453         ts.innerHTML = '';
36454         ts.appendChild(document.createTextNode(v));
36455         v = ts.innerHTML;
36456
36457         Roo.fly(ts).setWidth(this.el.getWidth());
36458         if(v.length < 1){
36459             v = "&#160;&#160;";
36460         }else{
36461             if(Roo.isIE){
36462                 v = v.replace(/\n/g, '<p>&#160;</p>');
36463             }
36464             v += "&#160;\n&#160;";
36465         }
36466         ts.innerHTML = v;
36467         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36468         if(h != this.lastHeight){
36469             this.lastHeight = h;
36470             this.el.setHeight(h);
36471             this.fireEvent("autosize", this, h);
36472         }
36473     }
36474 });/*
36475  * Based on:
36476  * Ext JS Library 1.1.1
36477  * Copyright(c) 2006-2007, Ext JS, LLC.
36478  *
36479  * Originally Released Under LGPL - original licence link has changed is not relivant.
36480  *
36481  * Fork - LGPL
36482  * <script type="text/javascript">
36483  */
36484  
36485
36486 /**
36487  * @class Roo.form.NumberField
36488  * @extends Roo.form.TextField
36489  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36490  * @constructor
36491  * Creates a new NumberField
36492  * @param {Object} config Configuration options
36493  */
36494 Roo.form.NumberField = function(config){
36495     Roo.form.NumberField.superclass.constructor.call(this, config);
36496 };
36497
36498 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36499     /**
36500      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36501      */
36502     fieldClass: "x-form-field x-form-num-field",
36503     /**
36504      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36505      */
36506     allowDecimals : true,
36507     /**
36508      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36509      */
36510     decimalSeparator : ".",
36511     /**
36512      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36513      */
36514     decimalPrecision : 2,
36515     /**
36516      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36517      */
36518     allowNegative : true,
36519     /**
36520      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36521      */
36522     minValue : Number.NEGATIVE_INFINITY,
36523     /**
36524      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36525      */
36526     maxValue : Number.MAX_VALUE,
36527     /**
36528      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36529      */
36530     minText : "The minimum value for this field is {0}",
36531     /**
36532      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36533      */
36534     maxText : "The maximum value for this field is {0}",
36535     /**
36536      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36537      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36538      */
36539     nanText : "{0} is not a valid number",
36540
36541     // private
36542     initEvents : function(){
36543         Roo.form.NumberField.superclass.initEvents.call(this);
36544         var allowed = "0123456789";
36545         if(this.allowDecimals){
36546             allowed += this.decimalSeparator;
36547         }
36548         if(this.allowNegative){
36549             allowed += "-";
36550         }
36551         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36552         var keyPress = function(e){
36553             var k = e.getKey();
36554             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36555                 return;
36556             }
36557             var c = e.getCharCode();
36558             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36559                 e.stopEvent();
36560             }
36561         };
36562         this.el.on("keypress", keyPress, this);
36563     },
36564
36565     // private
36566     validateValue : function(value){
36567         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36568             return false;
36569         }
36570         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36571              return true;
36572         }
36573         var num = this.parseValue(value);
36574         if(isNaN(num)){
36575             this.markInvalid(String.format(this.nanText, value));
36576             return false;
36577         }
36578         if(num < this.minValue){
36579             this.markInvalid(String.format(this.minText, this.minValue));
36580             return false;
36581         }
36582         if(num > this.maxValue){
36583             this.markInvalid(String.format(this.maxText, this.maxValue));
36584             return false;
36585         }
36586         return true;
36587     },
36588
36589     getValue : function(){
36590         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36591     },
36592
36593     // private
36594     parseValue : function(value){
36595         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36596         return isNaN(value) ? '' : value;
36597     },
36598
36599     // private
36600     fixPrecision : function(value){
36601         var nan = isNaN(value);
36602         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36603             return nan ? '' : value;
36604         }
36605         return parseFloat(value).toFixed(this.decimalPrecision);
36606     },
36607
36608     setValue : function(v){
36609         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36610     },
36611
36612     // private
36613     decimalPrecisionFcn : function(v){
36614         return Math.floor(v);
36615     },
36616
36617     beforeBlur : function(){
36618         var v = this.parseValue(this.getRawValue());
36619         if(v){
36620             this.setValue(this.fixPrecision(v));
36621         }
36622     }
36623 });/*
36624  * Based on:
36625  * Ext JS Library 1.1.1
36626  * Copyright(c) 2006-2007, Ext JS, LLC.
36627  *
36628  * Originally Released Under LGPL - original licence link has changed is not relivant.
36629  *
36630  * Fork - LGPL
36631  * <script type="text/javascript">
36632  */
36633  
36634 /**
36635  * @class Roo.form.DateField
36636  * @extends Roo.form.TriggerField
36637  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36638 * @constructor
36639 * Create a new DateField
36640 * @param {Object} config
36641  */
36642 Roo.form.DateField = function(config){
36643     Roo.form.DateField.superclass.constructor.call(this, config);
36644     
36645       this.addEvents({
36646          
36647         /**
36648          * @event select
36649          * Fires when a date is selected
36650              * @param {Roo.form.DateField} combo This combo box
36651              * @param {Date} date The date selected
36652              */
36653         'select' : true
36654          
36655     });
36656     
36657     
36658     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36659     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36660     this.ddMatch = null;
36661     if(this.disabledDates){
36662         var dd = this.disabledDates;
36663         var re = "(?:";
36664         for(var i = 0; i < dd.length; i++){
36665             re += dd[i];
36666             if(i != dd.length-1) re += "|";
36667         }
36668         this.ddMatch = new RegExp(re + ")");
36669     }
36670 };
36671
36672 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36673     /**
36674      * @cfg {String} format
36675      * The default date format string which can be overriden for localization support.  The format must be
36676      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36677      */
36678     format : "m/d/y",
36679     /**
36680      * @cfg {String} altFormats
36681      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36682      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36683      */
36684     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36685     /**
36686      * @cfg {Array} disabledDays
36687      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36688      */
36689     disabledDays : null,
36690     /**
36691      * @cfg {String} disabledDaysText
36692      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36693      */
36694     disabledDaysText : "Disabled",
36695     /**
36696      * @cfg {Array} disabledDates
36697      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36698      * expression so they are very powerful. Some examples:
36699      * <ul>
36700      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36701      * <li>["03/08", "09/16"] would disable those days for every year</li>
36702      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36703      * <li>["03/../2006"] would disable every day in March 2006</li>
36704      * <li>["^03"] would disable every day in every March</li>
36705      * </ul>
36706      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36707      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36708      */
36709     disabledDates : null,
36710     /**
36711      * @cfg {String} disabledDatesText
36712      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36713      */
36714     disabledDatesText : "Disabled",
36715     /**
36716      * @cfg {Date/String} minValue
36717      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36718      * valid format (defaults to null).
36719      */
36720     minValue : null,
36721     /**
36722      * @cfg {Date/String} maxValue
36723      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36724      * valid format (defaults to null).
36725      */
36726     maxValue : null,
36727     /**
36728      * @cfg {String} minText
36729      * The error text to display when the date in the cell is before minValue (defaults to
36730      * 'The date in this field must be after {minValue}').
36731      */
36732     minText : "The date in this field must be equal to or after {0}",
36733     /**
36734      * @cfg {String} maxText
36735      * The error text to display when the date in the cell is after maxValue (defaults to
36736      * 'The date in this field must be before {maxValue}').
36737      */
36738     maxText : "The date in this field must be equal to or before {0}",
36739     /**
36740      * @cfg {String} invalidText
36741      * The error text to display when the date in the field is invalid (defaults to
36742      * '{value} is not a valid date - it must be in the format {format}').
36743      */
36744     invalidText : "{0} is not a valid date - it must be in the format {1}",
36745     /**
36746      * @cfg {String} triggerClass
36747      * An additional CSS class used to style the trigger button.  The trigger will always get the
36748      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36749      * which displays a calendar icon).
36750      */
36751     triggerClass : 'x-form-date-trigger',
36752     
36753
36754     /**
36755      * @cfg {bool} useIso
36756      * if enabled, then the date field will use a hidden field to store the 
36757      * real value as iso formated date. default (false)
36758      */ 
36759     useIso : false,
36760     /**
36761      * @cfg {String/Object} autoCreate
36762      * A DomHelper element spec, or true for a default element spec (defaults to
36763      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36764      */ 
36765     // private
36766     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36767     
36768     // private
36769     hiddenField: false,
36770     
36771     onRender : function(ct, position)
36772     {
36773         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36774         if (this.useIso) {
36775             this.el.dom.removeAttribute('name'); 
36776             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36777                     'before', true);
36778             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36779             // prevent input submission
36780             this.hiddenName = this.name;
36781         }
36782             
36783             
36784     },
36785     
36786     // private
36787     validateValue : function(value)
36788     {
36789         value = this.formatDate(value);
36790         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36791             return false;
36792         }
36793         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36794              return true;
36795         }
36796         var svalue = value;
36797         value = this.parseDate(value);
36798         if(!value){
36799             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36800             return false;
36801         }
36802         var time = value.getTime();
36803         if(this.minValue && time < this.minValue.getTime()){
36804             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36805             return false;
36806         }
36807         if(this.maxValue && time > this.maxValue.getTime()){
36808             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36809             return false;
36810         }
36811         if(this.disabledDays){
36812             var day = value.getDay();
36813             for(var i = 0; i < this.disabledDays.length; i++) {
36814                 if(day === this.disabledDays[i]){
36815                     this.markInvalid(this.disabledDaysText);
36816                     return false;
36817                 }
36818             }
36819         }
36820         var fvalue = this.formatDate(value);
36821         if(this.ddMatch && this.ddMatch.test(fvalue)){
36822             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36823             return false;
36824         }
36825         return true;
36826     },
36827
36828     // private
36829     // Provides logic to override the default TriggerField.validateBlur which just returns true
36830     validateBlur : function(){
36831         return !this.menu || !this.menu.isVisible();
36832     },
36833
36834     /**
36835      * Returns the current date value of the date field.
36836      * @return {Date} The date value
36837      */
36838     getValue : function(){
36839         
36840         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36841     },
36842
36843     /**
36844      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36845      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36846      * (the default format used is "m/d/y").
36847      * <br />Usage:
36848      * <pre><code>
36849 //All of these calls set the same date value (May 4, 2006)
36850
36851 //Pass a date object:
36852 var dt = new Date('5/4/06');
36853 dateField.setValue(dt);
36854
36855 //Pass a date string (default format):
36856 dateField.setValue('5/4/06');
36857
36858 //Pass a date string (custom format):
36859 dateField.format = 'Y-m-d';
36860 dateField.setValue('2006-5-4');
36861 </code></pre>
36862      * @param {String/Date} date The date or valid date string
36863      */
36864     setValue : function(date){
36865         if (this.hiddenField) {
36866             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36867         }
36868         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36869     },
36870
36871     // private
36872     parseDate : function(value){
36873         if(!value || value instanceof Date){
36874             return value;
36875         }
36876         var v = Date.parseDate(value, this.format);
36877         if(!v && this.altFormats){
36878             if(!this.altFormatsArray){
36879                 this.altFormatsArray = this.altFormats.split("|");
36880             }
36881             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36882                 v = Date.parseDate(value, this.altFormatsArray[i]);
36883             }
36884         }
36885         return v;
36886     },
36887
36888     // private
36889     formatDate : function(date, fmt){
36890         return (!date || !(date instanceof Date)) ?
36891                date : date.dateFormat(fmt || this.format);
36892     },
36893
36894     // private
36895     menuListeners : {
36896         select: function(m, d){
36897             this.setValue(d);
36898             this.fireEvent('select', this, d);
36899         },
36900         show : function(){ // retain focus styling
36901             this.onFocus();
36902         },
36903         hide : function(){
36904             this.focus.defer(10, this);
36905             var ml = this.menuListeners;
36906             this.menu.un("select", ml.select,  this);
36907             this.menu.un("show", ml.show,  this);
36908             this.menu.un("hide", ml.hide,  this);
36909         }
36910     },
36911
36912     // private
36913     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36914     onTriggerClick : function(){
36915         if(this.disabled){
36916             return;
36917         }
36918         if(this.menu == null){
36919             this.menu = new Roo.menu.DateMenu();
36920         }
36921         Roo.apply(this.menu.picker,  {
36922             showClear: this.allowBlank,
36923             minDate : this.minValue,
36924             maxDate : this.maxValue,
36925             disabledDatesRE : this.ddMatch,
36926             disabledDatesText : this.disabledDatesText,
36927             disabledDays : this.disabledDays,
36928             disabledDaysText : this.disabledDaysText,
36929             format : this.format,
36930             minText : String.format(this.minText, this.formatDate(this.minValue)),
36931             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36932         });
36933         this.menu.on(Roo.apply({}, this.menuListeners, {
36934             scope:this
36935         }));
36936         this.menu.picker.setValue(this.getValue() || new Date());
36937         this.menu.show(this.el, "tl-bl?");
36938     },
36939
36940     beforeBlur : function(){
36941         var v = this.parseDate(this.getRawValue());
36942         if(v){
36943             this.setValue(v);
36944         }
36945     }
36946
36947     /** @cfg {Boolean} grow @hide */
36948     /** @cfg {Number} growMin @hide */
36949     /** @cfg {Number} growMax @hide */
36950     /**
36951      * @hide
36952      * @method autoSize
36953      */
36954 });/*
36955  * Based on:
36956  * Ext JS Library 1.1.1
36957  * Copyright(c) 2006-2007, Ext JS, LLC.
36958  *
36959  * Originally Released Under LGPL - original licence link has changed is not relivant.
36960  *
36961  * Fork - LGPL
36962  * <script type="text/javascript">
36963  */
36964  
36965
36966 /**
36967  * @class Roo.form.ComboBox
36968  * @extends Roo.form.TriggerField
36969  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36970  * @constructor
36971  * Create a new ComboBox.
36972  * @param {Object} config Configuration options
36973  */
36974 Roo.form.ComboBox = function(config){
36975     Roo.form.ComboBox.superclass.constructor.call(this, config);
36976     this.addEvents({
36977         /**
36978          * @event expand
36979          * Fires when the dropdown list is expanded
36980              * @param {Roo.form.ComboBox} combo This combo box
36981              */
36982         'expand' : true,
36983         /**
36984          * @event collapse
36985          * Fires when the dropdown list is collapsed
36986              * @param {Roo.form.ComboBox} combo This combo box
36987              */
36988         'collapse' : true,
36989         /**
36990          * @event beforeselect
36991          * Fires before a list item is selected. Return false to cancel the selection.
36992              * @param {Roo.form.ComboBox} combo This combo box
36993              * @param {Roo.data.Record} record The data record returned from the underlying store
36994              * @param {Number} index The index of the selected item in the dropdown list
36995              */
36996         'beforeselect' : true,
36997         /**
36998          * @event select
36999          * Fires when a list item is selected
37000              * @param {Roo.form.ComboBox} combo This combo box
37001              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37002              * @param {Number} index The index of the selected item in the dropdown list
37003              */
37004         'select' : true,
37005         /**
37006          * @event beforequery
37007          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37008          * The event object passed has these properties:
37009              * @param {Roo.form.ComboBox} combo This combo box
37010              * @param {String} query The query
37011              * @param {Boolean} forceAll true to force "all" query
37012              * @param {Boolean} cancel true to cancel the query
37013              * @param {Object} e The query event object
37014              */
37015         'beforequery': true,
37016          /**
37017          * @event add
37018          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37019              * @param {Roo.form.ComboBox} combo This combo box
37020              */
37021         'add' : true,
37022         /**
37023          * @event edit
37024          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37025              * @param {Roo.form.ComboBox} combo This combo box
37026              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37027              */
37028         'edit' : true
37029         
37030         
37031     });
37032     if(this.transform){
37033         this.allowDomMove = false;
37034         var s = Roo.getDom(this.transform);
37035         if(!this.hiddenName){
37036             this.hiddenName = s.name;
37037         }
37038         if(!this.store){
37039             this.mode = 'local';
37040             var d = [], opts = s.options;
37041             for(var i = 0, len = opts.length;i < len; i++){
37042                 var o = opts[i];
37043                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37044                 if(o.selected) {
37045                     this.value = value;
37046                 }
37047                 d.push([value, o.text]);
37048             }
37049             this.store = new Roo.data.SimpleStore({
37050                 'id': 0,
37051                 fields: ['value', 'text'],
37052                 data : d
37053             });
37054             this.valueField = 'value';
37055             this.displayField = 'text';
37056         }
37057         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37058         if(!this.lazyRender){
37059             this.target = true;
37060             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37061             s.parentNode.removeChild(s); // remove it
37062             this.render(this.el.parentNode);
37063         }else{
37064             s.parentNode.removeChild(s); // remove it
37065         }
37066
37067     }
37068     if (this.store) {
37069         this.store = Roo.factory(this.store, Roo.data);
37070     }
37071     
37072     this.selectedIndex = -1;
37073     if(this.mode == 'local'){
37074         if(config.queryDelay === undefined){
37075             this.queryDelay = 10;
37076         }
37077         if(config.minChars === undefined){
37078             this.minChars = 0;
37079         }
37080     }
37081 };
37082
37083 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37084     /**
37085      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37086      */
37087     /**
37088      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37089      * rendering into an Roo.Editor, defaults to false)
37090      */
37091     /**
37092      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37093      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37094      */
37095     /**
37096      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37097      */
37098     /**
37099      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37100      * the dropdown list (defaults to undefined, with no header element)
37101      */
37102
37103      /**
37104      * @cfg {String/Roo.Template} tpl The template to use to render the output
37105      */
37106      
37107     // private
37108     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37109     /**
37110      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37111      */
37112     listWidth: undefined,
37113     /**
37114      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37115      * mode = 'remote' or 'text' if mode = 'local')
37116      */
37117     displayField: undefined,
37118     /**
37119      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37120      * mode = 'remote' or 'value' if mode = 'local'). 
37121      * Note: use of a valueField requires the user make a selection
37122      * in order for a value to be mapped.
37123      */
37124     valueField: undefined,
37125     /**
37126      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37127      * field's data value (defaults to the underlying DOM element's name)
37128      */
37129     hiddenName: undefined,
37130     /**
37131      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37132      */
37133     listClass: '',
37134     /**
37135      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37136      */
37137     selectedClass: 'x-combo-selected',
37138     /**
37139      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37140      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37141      * which displays a downward arrow icon).
37142      */
37143     triggerClass : 'x-form-arrow-trigger',
37144     /**
37145      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37146      */
37147     shadow:'sides',
37148     /**
37149      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37150      * anchor positions (defaults to 'tl-bl')
37151      */
37152     listAlign: 'tl-bl?',
37153     /**
37154      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37155      */
37156     maxHeight: 300,
37157     /**
37158      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37159      * query specified by the allQuery config option (defaults to 'query')
37160      */
37161     triggerAction: 'query',
37162     /**
37163      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37164      * (defaults to 4, does not apply if editable = false)
37165      */
37166     minChars : 4,
37167     /**
37168      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37169      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37170      */
37171     typeAhead: false,
37172     /**
37173      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37174      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37175      */
37176     queryDelay: 500,
37177     /**
37178      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37179      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37180      */
37181     pageSize: 0,
37182     /**
37183      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37184      * when editable = true (defaults to false)
37185      */
37186     selectOnFocus:false,
37187     /**
37188      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37189      */
37190     queryParam: 'query',
37191     /**
37192      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37193      * when mode = 'remote' (defaults to 'Loading...')
37194      */
37195     loadingText: 'Loading...',
37196     /**
37197      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37198      */
37199     resizable: false,
37200     /**
37201      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37202      */
37203     handleHeight : 8,
37204     /**
37205      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37206      * traditional select (defaults to true)
37207      */
37208     editable: true,
37209     /**
37210      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37211      */
37212     allQuery: '',
37213     /**
37214      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37215      */
37216     mode: 'remote',
37217     /**
37218      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37219      * listWidth has a higher value)
37220      */
37221     minListWidth : 70,
37222     /**
37223      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37224      * allow the user to set arbitrary text into the field (defaults to false)
37225      */
37226     forceSelection:false,
37227     /**
37228      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37229      * if typeAhead = true (defaults to 250)
37230      */
37231     typeAheadDelay : 250,
37232     /**
37233      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37234      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37235      */
37236     valueNotFoundText : undefined,
37237     /**
37238      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37239      */
37240     blockFocus : false,
37241     
37242     /**
37243      * @cfg {Boolean} disableClear Disable showing of clear button.
37244      */
37245     disableClear : false,
37246     /**
37247      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37248      */
37249     alwaysQuery : false,
37250     
37251     //private
37252     addicon : false,
37253     editicon: false,
37254     
37255     
37256     // private
37257     onRender : function(ct, position){
37258         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37259         if(this.hiddenName){
37260             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37261                     'before', true);
37262             this.hiddenField.value =
37263                 this.hiddenValue !== undefined ? this.hiddenValue :
37264                 this.value !== undefined ? this.value : '';
37265
37266             // prevent input submission
37267             this.el.dom.removeAttribute('name');
37268         }
37269         if(Roo.isGecko){
37270             this.el.dom.setAttribute('autocomplete', 'off');
37271         }
37272
37273         var cls = 'x-combo-list';
37274
37275         this.list = new Roo.Layer({
37276             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37277         });
37278
37279         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37280         this.list.setWidth(lw);
37281         this.list.swallowEvent('mousewheel');
37282         this.assetHeight = 0;
37283
37284         if(this.title){
37285             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37286             this.assetHeight += this.header.getHeight();
37287         }
37288
37289         this.innerList = this.list.createChild({cls:cls+'-inner'});
37290         this.innerList.on('mouseover', this.onViewOver, this);
37291         this.innerList.on('mousemove', this.onViewMove, this);
37292         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37293         
37294         if(this.allowBlank && !this.pageSize && !this.disableClear){
37295             this.footer = this.list.createChild({cls:cls+'-ft'});
37296             this.pageTb = new Roo.Toolbar(this.footer);
37297            
37298         }
37299         if(this.pageSize){
37300             this.footer = this.list.createChild({cls:cls+'-ft'});
37301             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37302                     {pageSize: this.pageSize});
37303             
37304         }
37305         
37306         if (this.pageTb && this.allowBlank && !this.disableClear) {
37307             var _this = this;
37308             this.pageTb.add(new Roo.Toolbar.Fill(), {
37309                 cls: 'x-btn-icon x-btn-clear',
37310                 text: '&#160;',
37311                 handler: function()
37312                 {
37313                     _this.collapse();
37314                     _this.clearValue();
37315                     _this.onSelect(false, -1);
37316                 }
37317             });
37318         }
37319         if (this.footer) {
37320             this.assetHeight += this.footer.getHeight();
37321         }
37322         
37323
37324         if(!this.tpl){
37325             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37326         }
37327
37328         this.view = new Roo.View(this.innerList, this.tpl, {
37329             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37330         });
37331
37332         this.view.on('click', this.onViewClick, this);
37333
37334         this.store.on('beforeload', this.onBeforeLoad, this);
37335         this.store.on('load', this.onLoad, this);
37336         this.store.on('loadexception', this.collapse, this);
37337
37338         if(this.resizable){
37339             this.resizer = new Roo.Resizable(this.list,  {
37340                pinned:true, handles:'se'
37341             });
37342             this.resizer.on('resize', function(r, w, h){
37343                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37344                 this.listWidth = w;
37345                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37346                 this.restrictHeight();
37347             }, this);
37348             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37349         }
37350         if(!this.editable){
37351             this.editable = true;
37352             this.setEditable(false);
37353         }  
37354         
37355         
37356         if (typeof(this.events.add.listeners) != 'undefined') {
37357             
37358             this.addicon = this.wrap.createChild(
37359                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37360        
37361             this.addicon.on('click', function(e) {
37362                 this.fireEvent('add', this);
37363             }, this);
37364         }
37365         if (typeof(this.events.edit.listeners) != 'undefined') {
37366             
37367             this.editicon = this.wrap.createChild(
37368                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37369             if (this.addicon) {
37370                 this.editicon.setStyle('margin-left', '40px');
37371             }
37372             this.editicon.on('click', function(e) {
37373                 
37374                 // we fire even  if inothing is selected..
37375                 this.fireEvent('edit', this, this.lastData );
37376                 
37377             }, this);
37378         }
37379         
37380         
37381         
37382     },
37383
37384     // private
37385     initEvents : function(){
37386         Roo.form.ComboBox.superclass.initEvents.call(this);
37387
37388         this.keyNav = new Roo.KeyNav(this.el, {
37389             "up" : function(e){
37390                 this.inKeyMode = true;
37391                 this.selectPrev();
37392             },
37393
37394             "down" : function(e){
37395                 if(!this.isExpanded()){
37396                     this.onTriggerClick();
37397                 }else{
37398                     this.inKeyMode = true;
37399                     this.selectNext();
37400                 }
37401             },
37402
37403             "enter" : function(e){
37404                 this.onViewClick();
37405                 //return true;
37406             },
37407
37408             "esc" : function(e){
37409                 this.collapse();
37410             },
37411
37412             "tab" : function(e){
37413                 this.onViewClick(false);
37414                 return true;
37415             },
37416
37417             scope : this,
37418
37419             doRelay : function(foo, bar, hname){
37420                 if(hname == 'down' || this.scope.isExpanded()){
37421                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37422                 }
37423                 return true;
37424             },
37425
37426             forceKeyDown: true
37427         });
37428         this.queryDelay = Math.max(this.queryDelay || 10,
37429                 this.mode == 'local' ? 10 : 250);
37430         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37431         if(this.typeAhead){
37432             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37433         }
37434         if(this.editable !== false){
37435             this.el.on("keyup", this.onKeyUp, this);
37436         }
37437         if(this.forceSelection){
37438             this.on('blur', this.doForce, this);
37439         }
37440     },
37441
37442     onDestroy : function(){
37443         if(this.view){
37444             this.view.setStore(null);
37445             this.view.el.removeAllListeners();
37446             this.view.el.remove();
37447             this.view.purgeListeners();
37448         }
37449         if(this.list){
37450             this.list.destroy();
37451         }
37452         if(this.store){
37453             this.store.un('beforeload', this.onBeforeLoad, this);
37454             this.store.un('load', this.onLoad, this);
37455             this.store.un('loadexception', this.collapse, this);
37456         }
37457         Roo.form.ComboBox.superclass.onDestroy.call(this);
37458     },
37459
37460     // private
37461     fireKey : function(e){
37462         if(e.isNavKeyPress() && !this.list.isVisible()){
37463             this.fireEvent("specialkey", this, e);
37464         }
37465     },
37466
37467     // private
37468     onResize: function(w, h){
37469         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37470         
37471         if(typeof w != 'number'){
37472             // we do not handle it!?!?
37473             return;
37474         }
37475         var tw = this.trigger.getWidth();
37476         tw += this.addicon ? this.addicon.getWidth() : 0;
37477         tw += this.editicon ? this.editicon.getWidth() : 0;
37478         var x = w - tw;
37479         this.el.setWidth( this.adjustWidth('input', x));
37480             
37481         this.trigger.setStyle('left', x+'px');
37482         
37483         if(this.list && this.listWidth === undefined){
37484             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37485             this.list.setWidth(lw);
37486             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37487         }
37488         
37489     
37490         
37491     },
37492
37493     /**
37494      * Allow or prevent the user from directly editing the field text.  If false is passed,
37495      * the user will only be able to select from the items defined in the dropdown list.  This method
37496      * is the runtime equivalent of setting the 'editable' config option at config time.
37497      * @param {Boolean} value True to allow the user to directly edit the field text
37498      */
37499     setEditable : function(value){
37500         if(value == this.editable){
37501             return;
37502         }
37503         this.editable = value;
37504         if(!value){
37505             this.el.dom.setAttribute('readOnly', true);
37506             this.el.on('mousedown', this.onTriggerClick,  this);
37507             this.el.addClass('x-combo-noedit');
37508         }else{
37509             this.el.dom.setAttribute('readOnly', false);
37510             this.el.un('mousedown', this.onTriggerClick,  this);
37511             this.el.removeClass('x-combo-noedit');
37512         }
37513     },
37514
37515     // private
37516     onBeforeLoad : function(){
37517         if(!this.hasFocus){
37518             return;
37519         }
37520         this.innerList.update(this.loadingText ?
37521                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37522         this.restrictHeight();
37523         this.selectedIndex = -1;
37524     },
37525
37526     // private
37527     onLoad : function(){
37528         if(!this.hasFocus){
37529             return;
37530         }
37531         if(this.store.getCount() > 0){
37532             this.expand();
37533             this.restrictHeight();
37534             if(this.lastQuery == this.allQuery){
37535                 if(this.editable){
37536                     this.el.dom.select();
37537                 }
37538                 if(!this.selectByValue(this.value, true)){
37539                     this.select(0, true);
37540                 }
37541             }else{
37542                 this.selectNext();
37543                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37544                     this.taTask.delay(this.typeAheadDelay);
37545                 }
37546             }
37547         }else{
37548             this.onEmptyResults();
37549         }
37550         //this.el.focus();
37551     },
37552
37553     // private
37554     onTypeAhead : function(){
37555         if(this.store.getCount() > 0){
37556             var r = this.store.getAt(0);
37557             var newValue = r.data[this.displayField];
37558             var len = newValue.length;
37559             var selStart = this.getRawValue().length;
37560             if(selStart != len){
37561                 this.setRawValue(newValue);
37562                 this.selectText(selStart, newValue.length);
37563             }
37564         }
37565     },
37566
37567     // private
37568     onSelect : function(record, index){
37569         if(this.fireEvent('beforeselect', this, record, index) !== false){
37570             this.setFromData(index > -1 ? record.data : false);
37571             this.collapse();
37572             this.fireEvent('select', this, record, index);
37573         }
37574     },
37575
37576     /**
37577      * Returns the currently selected field value or empty string if no value is set.
37578      * @return {String} value The selected value
37579      */
37580     getValue : function(){
37581         if(this.valueField){
37582             return typeof this.value != 'undefined' ? this.value : '';
37583         }else{
37584             return Roo.form.ComboBox.superclass.getValue.call(this);
37585         }
37586     },
37587
37588     /**
37589      * Clears any text/value currently set in the field
37590      */
37591     clearValue : function(){
37592         if(this.hiddenField){
37593             this.hiddenField.value = '';
37594         }
37595         this.value = '';
37596         this.setRawValue('');
37597         this.lastSelectionText = '';
37598         this.applyEmptyText();
37599     },
37600
37601     /**
37602      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37603      * will be displayed in the field.  If the value does not match the data value of an existing item,
37604      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37605      * Otherwise the field will be blank (although the value will still be set).
37606      * @param {String} value The value to match
37607      */
37608     setValue : function(v){
37609         var text = v;
37610         if(this.valueField){
37611             var r = this.findRecord(this.valueField, v);
37612             if(r){
37613                 text = r.data[this.displayField];
37614             }else if(this.valueNotFoundText !== undefined){
37615                 text = this.valueNotFoundText;
37616             }
37617         }
37618         this.lastSelectionText = text;
37619         if(this.hiddenField){
37620             this.hiddenField.value = v;
37621         }
37622         Roo.form.ComboBox.superclass.setValue.call(this, text);
37623         this.value = v;
37624     },
37625     /**
37626      * @property {Object} the last set data for the element
37627      */
37628     
37629     lastData : false,
37630     /**
37631      * Sets the value of the field based on a object which is related to the record format for the store.
37632      * @param {Object} value the value to set as. or false on reset?
37633      */
37634     setFromData : function(o){
37635         var dv = ''; // display value
37636         var vv = ''; // value value..
37637         this.lastData = o;
37638         if (this.displayField) {
37639             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37640         } else {
37641             // this is an error condition!!!
37642             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37643         }
37644         
37645         if(this.valueField){
37646             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37647         }
37648         if(this.hiddenField){
37649             this.hiddenField.value = vv;
37650             
37651             this.lastSelectionText = dv;
37652             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37653             this.value = vv;
37654             return;
37655         }
37656         // no hidden field.. - we store the value in 'value', but still display
37657         // display field!!!!
37658         this.lastSelectionText = dv;
37659         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37660         this.value = vv;
37661         
37662         
37663     },
37664     // private
37665     reset : function(){
37666         // overridden so that last data is reset..
37667         this.setValue(this.originalValue);
37668         this.clearInvalid();
37669         this.lastData = false;
37670     },
37671     // private
37672     findRecord : function(prop, value){
37673         var record;
37674         if(this.store.getCount() > 0){
37675             this.store.each(function(r){
37676                 if(r.data[prop] == value){
37677                     record = r;
37678                     return false;
37679                 }
37680             });
37681         }
37682         return record;
37683     },
37684
37685     // private
37686     onViewMove : function(e, t){
37687         this.inKeyMode = false;
37688     },
37689
37690     // private
37691     onViewOver : function(e, t){
37692         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37693             return;
37694         }
37695         var item = this.view.findItemFromChild(t);
37696         if(item){
37697             var index = this.view.indexOf(item);
37698             this.select(index, false);
37699         }
37700     },
37701
37702     // private
37703     onViewClick : function(doFocus){
37704         var index = this.view.getSelectedIndexes()[0];
37705         var r = this.store.getAt(index);
37706         if(r){
37707             this.onSelect(r, index);
37708         }
37709         if(doFocus !== false && !this.blockFocus){
37710             this.el.focus();
37711         }
37712     },
37713
37714     // private
37715     restrictHeight : function(){
37716         this.innerList.dom.style.height = '';
37717         var inner = this.innerList.dom;
37718         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37719         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37720         this.list.beginUpdate();
37721         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37722         this.list.alignTo(this.el, this.listAlign);
37723         this.list.endUpdate();
37724     },
37725
37726     // private
37727     onEmptyResults : function(){
37728         this.collapse();
37729     },
37730
37731     /**
37732      * Returns true if the dropdown list is expanded, else false.
37733      */
37734     isExpanded : function(){
37735         return this.list.isVisible();
37736     },
37737
37738     /**
37739      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37740      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37741      * @param {String} value The data value of the item to select
37742      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37743      * selected item if it is not currently in view (defaults to true)
37744      * @return {Boolean} True if the value matched an item in the list, else false
37745      */
37746     selectByValue : function(v, scrollIntoView){
37747         if(v !== undefined && v !== null){
37748             var r = this.findRecord(this.valueField || this.displayField, v);
37749             if(r){
37750                 this.select(this.store.indexOf(r), scrollIntoView);
37751                 return true;
37752             }
37753         }
37754         return false;
37755     },
37756
37757     /**
37758      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37759      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37760      * @param {Number} index The zero-based index of the list item to select
37761      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37762      * selected item if it is not currently in view (defaults to true)
37763      */
37764     select : function(index, scrollIntoView){
37765         this.selectedIndex = index;
37766         this.view.select(index);
37767         if(scrollIntoView !== false){
37768             var el = this.view.getNode(index);
37769             if(el){
37770                 this.innerList.scrollChildIntoView(el, false);
37771             }
37772         }
37773     },
37774
37775     // private
37776     selectNext : 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 < ct-1){
37782                 this.select(this.selectedIndex+1);
37783             }
37784         }
37785     },
37786
37787     // private
37788     selectPrev : function(){
37789         var ct = this.store.getCount();
37790         if(ct > 0){
37791             if(this.selectedIndex == -1){
37792                 this.select(0);
37793             }else if(this.selectedIndex != 0){
37794                 this.select(this.selectedIndex-1);
37795             }
37796         }
37797     },
37798
37799     // private
37800     onKeyUp : function(e){
37801         if(this.editable !== false && !e.isSpecialKey()){
37802             this.lastKey = e.getKey();
37803             this.dqTask.delay(this.queryDelay);
37804         }
37805     },
37806
37807     // private
37808     validateBlur : function(){
37809         return !this.list || !this.list.isVisible();   
37810     },
37811
37812     // private
37813     initQuery : function(){
37814         this.doQuery(this.getRawValue());
37815     },
37816
37817     // private
37818     doForce : function(){
37819         if(this.el.dom.value.length > 0){
37820             this.el.dom.value =
37821                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37822             this.applyEmptyText();
37823         }
37824     },
37825
37826     /**
37827      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37828      * query allowing the query action to be canceled if needed.
37829      * @param {String} query The SQL query to execute
37830      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37831      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37832      * saved in the current store (defaults to false)
37833      */
37834     doQuery : function(q, forceAll){
37835         if(q === undefined || q === null){
37836             q = '';
37837         }
37838         var qe = {
37839             query: q,
37840             forceAll: forceAll,
37841             combo: this,
37842             cancel:false
37843         };
37844         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37845             return false;
37846         }
37847         q = qe.query;
37848         forceAll = qe.forceAll;
37849         if(forceAll === true || (q.length >= this.minChars)){
37850             if(this.lastQuery != q || this.alwaysQuery){
37851                 this.lastQuery = q;
37852                 if(this.mode == 'local'){
37853                     this.selectedIndex = -1;
37854                     if(forceAll){
37855                         this.store.clearFilter();
37856                     }else{
37857                         this.store.filter(this.displayField, q);
37858                     }
37859                     this.onLoad();
37860                 }else{
37861                     this.store.baseParams[this.queryParam] = q;
37862                     this.store.load({
37863                         params: this.getParams(q)
37864                     });
37865                     this.expand();
37866                 }
37867             }else{
37868                 this.selectedIndex = -1;
37869                 this.onLoad();   
37870             }
37871         }
37872     },
37873
37874     // private
37875     getParams : function(q){
37876         var p = {};
37877         //p[this.queryParam] = q;
37878         if(this.pageSize){
37879             p.start = 0;
37880             p.limit = this.pageSize;
37881         }
37882         return p;
37883     },
37884
37885     /**
37886      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37887      */
37888     collapse : function(){
37889         if(!this.isExpanded()){
37890             return;
37891         }
37892         this.list.hide();
37893         Roo.get(document).un('mousedown', this.collapseIf, this);
37894         Roo.get(document).un('mousewheel', this.collapseIf, this);
37895         if (!this.editable) {
37896             Roo.get(document).un('keydown', this.listKeyPress, this);
37897         }
37898         this.fireEvent('collapse', this);
37899     },
37900
37901     // private
37902     collapseIf : function(e){
37903         if(!e.within(this.wrap) && !e.within(this.list)){
37904             this.collapse();
37905         }
37906     },
37907
37908     /**
37909      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37910      */
37911     expand : function(){
37912         if(this.isExpanded() || !this.hasFocus){
37913             return;
37914         }
37915         this.list.alignTo(this.el, this.listAlign);
37916         this.list.show();
37917         Roo.get(document).on('mousedown', this.collapseIf, this);
37918         Roo.get(document).on('mousewheel', this.collapseIf, this);
37919         if (!this.editable) {
37920             Roo.get(document).on('keydown', this.listKeyPress, this);
37921         }
37922         
37923         this.fireEvent('expand', this);
37924     },
37925
37926     // private
37927     // Implements the default empty TriggerField.onTriggerClick function
37928     onTriggerClick : function(){
37929         if(this.disabled){
37930             return;
37931         }
37932         if(this.isExpanded()){
37933             this.collapse();
37934             if (!this.blockFocus) {
37935                 this.el.focus();
37936             }
37937             
37938         }else {
37939             this.hasFocus = true;
37940             if(this.triggerAction == 'all') {
37941                 this.doQuery(this.allQuery, true);
37942             } else {
37943                 this.doQuery(this.getRawValue());
37944             }
37945             if (!this.blockFocus) {
37946                 this.el.focus();
37947             }
37948         }
37949     },
37950     listKeyPress : function(e)
37951     {
37952         //Roo.log('listkeypress');
37953         // scroll to first matching element based on key pres..
37954         if (e.isSpecialKey()) {
37955             return false;
37956         }
37957         var k = String.fromCharCode(e.getKey()).toUpperCase();
37958         //Roo.log(k);
37959         var match  = false;
37960         var csel = this.view.getSelectedNodes();
37961         var cselitem = false;
37962         if (csel.length) {
37963             var ix = this.view.indexOf(csel[0]);
37964             cselitem  = this.store.getAt(ix);
37965             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37966                 cselitem = false;
37967             }
37968             
37969         }
37970         
37971         this.store.each(function(v) { 
37972             if (cselitem) {
37973                 // start at existing selection.
37974                 if (cselitem.id == v.id) {
37975                     cselitem = false;
37976                 }
37977                 return;
37978             }
37979                 
37980             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37981                 match = this.store.indexOf(v);
37982                 return false;
37983             }
37984         }, this);
37985         
37986         if (match === false) {
37987             return true; // no more action?
37988         }
37989         // scroll to?
37990         this.view.select(match);
37991         var sn = Roo.get(this.view.getSelectedNodes()[0])
37992         sn.scrollIntoView(sn.dom.parentNode, false);
37993     }
37994
37995     /** 
37996     * @cfg {Boolean} grow 
37997     * @hide 
37998     */
37999     /** 
38000     * @cfg {Number} growMin 
38001     * @hide 
38002     */
38003     /** 
38004     * @cfg {Number} growMax 
38005     * @hide 
38006     */
38007     /**
38008      * @hide
38009      * @method autoSize
38010      */
38011 });/*
38012  * Based on:
38013  * Ext JS Library 1.1.1
38014  * Copyright(c) 2006-2007, Ext JS, LLC.
38015  *
38016  * Originally Released Under LGPL - original licence link has changed is not relivant.
38017  *
38018  * Fork - LGPL
38019  * <script type="text/javascript">
38020  */
38021 /**
38022  * @class Roo.form.Checkbox
38023  * @extends Roo.form.Field
38024  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38025  * @constructor
38026  * Creates a new Checkbox
38027  * @param {Object} config Configuration options
38028  */
38029 Roo.form.Checkbox = function(config){
38030     Roo.form.Checkbox.superclass.constructor.call(this, config);
38031     this.addEvents({
38032         /**
38033          * @event check
38034          * Fires when the checkbox is checked or unchecked.
38035              * @param {Roo.form.Checkbox} this This checkbox
38036              * @param {Boolean} checked The new checked value
38037              */
38038         check : true
38039     });
38040 };
38041
38042 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38043     /**
38044      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38045      */
38046     focusClass : undefined,
38047     /**
38048      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38049      */
38050     fieldClass: "x-form-field",
38051     /**
38052      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38053      */
38054     checked: false,
38055     /**
38056      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38057      * {tag: "input", type: "checkbox", autocomplete: "off"})
38058      */
38059     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38060     /**
38061      * @cfg {String} boxLabel The text that appears beside the checkbox
38062      */
38063     boxLabel : "",
38064     /**
38065      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38066      */  
38067     inputValue : '1',
38068     /**
38069      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38070      */
38071      valueOff: '0', // value when not checked..
38072
38073     actionMode : 'viewEl', 
38074     //
38075     // private
38076     itemCls : 'x-menu-check-item x-form-item',
38077     groupClass : 'x-menu-group-item',
38078     inputType : 'hidden',
38079     
38080     
38081     inSetChecked: false, // check that we are not calling self...
38082     
38083     inputElement: false, // real input element?
38084     basedOn: false, // ????
38085     
38086     isFormField: true, // not sure where this is needed!!!!
38087
38088     onResize : function(){
38089         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38090         if(!this.boxLabel){
38091             this.el.alignTo(this.wrap, 'c-c');
38092         }
38093     },
38094
38095     initEvents : function(){
38096         Roo.form.Checkbox.superclass.initEvents.call(this);
38097         this.el.on("click", this.onClick,  this);
38098         this.el.on("change", this.onClick,  this);
38099     },
38100
38101
38102     getResizeEl : function(){
38103         return this.wrap;
38104     },
38105
38106     getPositionEl : function(){
38107         return this.wrap;
38108     },
38109
38110     // private
38111     onRender : function(ct, position){
38112         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38113         /*
38114         if(this.inputValue !== undefined){
38115             this.el.dom.value = this.inputValue;
38116         }
38117         */
38118         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38119         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38120         var viewEl = this.wrap.createChild({ 
38121             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38122         this.viewEl = viewEl;   
38123         this.wrap.on('click', this.onClick,  this); 
38124         
38125         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38126         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38127         
38128         
38129         
38130         if(this.boxLabel){
38131             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38132         //    viewEl.on('click', this.onClick,  this); 
38133         }
38134         //if(this.checked){
38135             this.setChecked(this.checked);
38136         //}else{
38137             //this.checked = this.el.dom;
38138         //}
38139
38140     },
38141
38142     // private
38143     initValue : Roo.emptyFn,
38144
38145     /**
38146      * Returns the checked state of the checkbox.
38147      * @return {Boolean} True if checked, else false
38148      */
38149     getValue : function(){
38150         if(this.el){
38151             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38152         }
38153         return this.valueOff;
38154         
38155     },
38156
38157         // private
38158     onClick : function(){ 
38159         this.setChecked(!this.checked);
38160
38161         //if(this.el.dom.checked != this.checked){
38162         //    this.setValue(this.el.dom.checked);
38163        // }
38164     },
38165
38166     /**
38167      * Sets the checked state of the checkbox.
38168      * On is always based on a string comparison between inputValue and the param.
38169      * @param {Boolean/String} value - the value to set 
38170      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38171      */
38172     setValue : function(v,suppressEvent){
38173         
38174         
38175         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38176         //if(this.el && this.el.dom){
38177         //    this.el.dom.checked = this.checked;
38178         //    this.el.dom.defaultChecked = this.checked;
38179         //}
38180         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38181         //this.fireEvent("check", this, this.checked);
38182     },
38183     // private..
38184     setChecked : function(state,suppressEvent)
38185     {
38186         if (this.inSetChecked) {
38187             this.checked = state;
38188             return;
38189         }
38190         
38191     
38192         if(this.wrap){
38193             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38194         }
38195         this.checked = state;
38196         if(suppressEvent !== true){
38197             this.fireEvent('check', this, state);
38198         }
38199         this.inSetChecked = true;
38200         this.el.dom.value = state ? this.inputValue : this.valueOff;
38201         this.inSetChecked = false;
38202         
38203     },
38204     // handle setting of hidden value by some other method!!?!?
38205     setFromHidden: function()
38206     {
38207         if(!this.el){
38208             return;
38209         }
38210         //console.log("SET FROM HIDDEN");
38211         //alert('setFrom hidden');
38212         this.setValue(this.el.dom.value);
38213     },
38214     
38215     onDestroy : function()
38216     {
38217         if(this.viewEl){
38218             Roo.get(this.viewEl).remove();
38219         }
38220          
38221         Roo.form.Checkbox.superclass.onDestroy.call(this);
38222     }
38223
38224 });/*
38225  * Based on:
38226  * Ext JS Library 1.1.1
38227  * Copyright(c) 2006-2007, Ext JS, LLC.
38228  *
38229  * Originally Released Under LGPL - original licence link has changed is not relivant.
38230  *
38231  * Fork - LGPL
38232  * <script type="text/javascript">
38233  */
38234  
38235 /**
38236  * @class Roo.form.Radio
38237  * @extends Roo.form.Checkbox
38238  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38239  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38240  * @constructor
38241  * Creates a new Radio
38242  * @param {Object} config Configuration options
38243  */
38244 Roo.form.Radio = function(){
38245     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38246 };
38247 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38248     inputType: 'radio',
38249
38250     /**
38251      * If this radio is part of a group, it will return the selected value
38252      * @return {String}
38253      */
38254     getGroupValue : function(){
38255         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38256     }
38257 });//<script type="text/javascript">
38258
38259 /*
38260  * Ext JS Library 1.1.1
38261  * Copyright(c) 2006-2007, Ext JS, LLC.
38262  * licensing@extjs.com
38263  * 
38264  * http://www.extjs.com/license
38265  */
38266  
38267  /*
38268   * 
38269   * Known bugs:
38270   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38271   * - IE ? - no idea how much works there.
38272   * 
38273   * 
38274   * 
38275   */
38276  
38277
38278 /**
38279  * @class Ext.form.HtmlEditor
38280  * @extends Ext.form.Field
38281  * Provides a lightweight HTML Editor component.
38282  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38283  * 
38284  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38285  * supported by this editor.</b><br/><br/>
38286  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38287  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38288  */
38289 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38290       /**
38291      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38292      */
38293     toolbars : false,
38294     /**
38295      * @cfg {String} createLinkText The default text for the create link prompt
38296      */
38297     createLinkText : 'Please enter the URL for the link:',
38298     /**
38299      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38300      */
38301     defaultLinkValue : 'http:/'+'/',
38302    
38303     
38304     // id of frame..
38305     frameId: false,
38306     
38307     // private properties
38308     validationEvent : false,
38309     deferHeight: true,
38310     initialized : false,
38311     activated : false,
38312     sourceEditMode : false,
38313     onFocus : Roo.emptyFn,
38314     iframePad:3,
38315     hideMode:'offsets',
38316     defaultAutoCreate : {
38317         tag: "textarea",
38318         style:"width:500px;height:300px;",
38319         autocomplete: "off"
38320     },
38321
38322     // private
38323     initComponent : function(){
38324         this.addEvents({
38325             /**
38326              * @event initialize
38327              * Fires when the editor is fully initialized (including the iframe)
38328              * @param {HtmlEditor} this
38329              */
38330             initialize: true,
38331             /**
38332              * @event activate
38333              * Fires when the editor is first receives the focus. Any insertion must wait
38334              * until after this event.
38335              * @param {HtmlEditor} this
38336              */
38337             activate: true,
38338              /**
38339              * @event beforesync
38340              * Fires before the textarea is updated with content from the editor iframe. Return false
38341              * to cancel the sync.
38342              * @param {HtmlEditor} this
38343              * @param {String} html
38344              */
38345             beforesync: true,
38346              /**
38347              * @event beforepush
38348              * Fires before the iframe editor is updated with content from the textarea. Return false
38349              * to cancel the push.
38350              * @param {HtmlEditor} this
38351              * @param {String} html
38352              */
38353             beforepush: true,
38354              /**
38355              * @event sync
38356              * Fires when the textarea is updated with content from the editor iframe.
38357              * @param {HtmlEditor} this
38358              * @param {String} html
38359              */
38360             sync: true,
38361              /**
38362              * @event push
38363              * Fires when the iframe editor is updated with content from the textarea.
38364              * @param {HtmlEditor} this
38365              * @param {String} html
38366              */
38367             push: true,
38368              /**
38369              * @event editmodechange
38370              * Fires when the editor switches edit modes
38371              * @param {HtmlEditor} this
38372              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38373              */
38374             editmodechange: true,
38375             /**
38376              * @event editorevent
38377              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38378              * @param {HtmlEditor} this
38379              */
38380             editorevent: true
38381         })
38382     },
38383
38384     /**
38385      * Protected method that will not generally be called directly. It
38386      * is called when the editor creates its toolbar. Override this method if you need to
38387      * add custom toolbar buttons.
38388      * @param {HtmlEditor} editor
38389      */
38390     createToolbar : function(editor){
38391         if (!editor.toolbars || !editor.toolbars.length) {
38392             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38393         }
38394         
38395         for (var i =0 ; i < editor.toolbars.length;i++) {
38396             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38397             editor.toolbars[i].init(editor);
38398         }
38399          
38400         
38401     },
38402
38403     /**
38404      * Protected method that will not generally be called directly. It
38405      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38406      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38407      */
38408     getDocMarkup : function(){
38409         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38410     },
38411
38412     // private
38413     onRender : function(ct, position){
38414         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38415         this.el.dom.style.border = '0 none';
38416         this.el.dom.setAttribute('tabIndex', -1);
38417         this.el.addClass('x-hidden');
38418         if(Roo.isIE){ // fix IE 1px bogus margin
38419             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38420         }
38421         this.wrap = this.el.wrap({
38422             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38423         });
38424
38425         this.frameId = Roo.id();
38426         this.createToolbar(this);
38427         
38428         
38429         
38430         
38431       
38432         
38433         var iframe = this.wrap.createChild({
38434             tag: 'iframe',
38435             id: this.frameId,
38436             name: this.frameId,
38437             frameBorder : 'no',
38438             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38439         });
38440         
38441        // console.log(iframe);
38442         //this.wrap.dom.appendChild(iframe);
38443
38444         this.iframe = iframe.dom;
38445
38446          this.assignDocWin();
38447         
38448         this.doc.designMode = 'on';
38449        
38450         this.doc.open();
38451         this.doc.write(this.getDocMarkup());
38452         this.doc.close();
38453
38454         
38455         var task = { // must defer to wait for browser to be ready
38456             run : function(){
38457                 //console.log("run task?" + this.doc.readyState);
38458                 this.assignDocWin();
38459                 if(this.doc.body || this.doc.readyState == 'complete'){
38460                     try {
38461                         this.doc.designMode="on";
38462                     } catch (e) {
38463                         return;
38464                     }
38465                     Roo.TaskMgr.stop(task);
38466                     this.initEditor.defer(10, this);
38467                 }
38468             },
38469             interval : 10,
38470             duration:10000,
38471             scope: this
38472         };
38473         Roo.TaskMgr.start(task);
38474
38475         if(!this.width){
38476             this.setSize(this.el.getSize());
38477         }
38478     },
38479
38480     // private
38481     onResize : function(w, h){
38482         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38483         if(this.el && this.iframe){
38484             if(typeof w == 'number'){
38485                 var aw = w - this.wrap.getFrameWidth('lr');
38486                 this.el.setWidth(this.adjustWidth('textarea', aw));
38487                 this.iframe.style.width = aw + 'px';
38488             }
38489             if(typeof h == 'number'){
38490                 var tbh = 0;
38491                 for (var i =0; i < this.toolbars.length;i++) {
38492                     // fixme - ask toolbars for heights?
38493                     tbh += this.toolbars[i].tb.el.getHeight();
38494                 }
38495                 
38496                 
38497                 
38498                 
38499                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38500                 this.el.setHeight(this.adjustWidth('textarea', ah));
38501                 this.iframe.style.height = ah + 'px';
38502                 if(this.doc){
38503                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38504                 }
38505             }
38506         }
38507     },
38508
38509     /**
38510      * Toggles the editor between standard and source edit mode.
38511      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38512      */
38513     toggleSourceEdit : function(sourceEditMode){
38514         
38515         this.sourceEditMode = sourceEditMode === true;
38516         
38517         if(this.sourceEditMode){
38518           
38519             this.syncValue();
38520             this.iframe.className = 'x-hidden';
38521             this.el.removeClass('x-hidden');
38522             this.el.dom.removeAttribute('tabIndex');
38523             this.el.focus();
38524         }else{
38525              
38526             this.pushValue();
38527             this.iframe.className = '';
38528             this.el.addClass('x-hidden');
38529             this.el.dom.setAttribute('tabIndex', -1);
38530             this.deferFocus();
38531         }
38532         this.setSize(this.wrap.getSize());
38533         this.fireEvent('editmodechange', this, this.sourceEditMode);
38534     },
38535
38536     // private used internally
38537     createLink : function(){
38538         var url = prompt(this.createLinkText, this.defaultLinkValue);
38539         if(url && url != 'http:/'+'/'){
38540             this.relayCmd('createlink', url);
38541         }
38542     },
38543
38544     // private (for BoxComponent)
38545     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38546
38547     // private (for BoxComponent)
38548     getResizeEl : function(){
38549         return this.wrap;
38550     },
38551
38552     // private (for BoxComponent)
38553     getPositionEl : function(){
38554         return this.wrap;
38555     },
38556
38557     // private
38558     initEvents : function(){
38559         this.originalValue = this.getValue();
38560     },
38561
38562     /**
38563      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38564      * @method
38565      */
38566     markInvalid : Roo.emptyFn,
38567     /**
38568      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38569      * @method
38570      */
38571     clearInvalid : Roo.emptyFn,
38572
38573     setValue : function(v){
38574         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38575         this.pushValue();
38576     },
38577
38578     /**
38579      * Protected method that will not generally be called directly. If you need/want
38580      * custom HTML cleanup, this is the method you should override.
38581      * @param {String} html The HTML to be cleaned
38582      * return {String} The cleaned HTML
38583      */
38584     cleanHtml : function(html){
38585         html = String(html);
38586         if(html.length > 5){
38587             if(Roo.isSafari){ // strip safari nonsense
38588                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38589             }
38590         }
38591         if(html == '&nbsp;'){
38592             html = '';
38593         }
38594         return html;
38595     },
38596
38597     /**
38598      * Protected method that will not generally be called directly. Syncs the contents
38599      * of the editor iframe with the textarea.
38600      */
38601     syncValue : function(){
38602         if(this.initialized){
38603             var bd = (this.doc.body || this.doc.documentElement);
38604             var html = bd.innerHTML;
38605             if(Roo.isSafari){
38606                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38607                 var m = bs.match(/text-align:(.*?);/i);
38608                 if(m && m[1]){
38609                     html = '<div style="'+m[0]+'">' + html + '</div>';
38610                 }
38611             }
38612             html = this.cleanHtml(html);
38613             if(this.fireEvent('beforesync', this, html) !== false){
38614                 this.el.dom.value = html;
38615                 this.fireEvent('sync', this, html);
38616             }
38617         }
38618     },
38619
38620     /**
38621      * Protected method that will not generally be called directly. Pushes the value of the textarea
38622      * into the iframe editor.
38623      */
38624     pushValue : function(){
38625         if(this.initialized){
38626             var v = this.el.dom.value;
38627             if(v.length < 1){
38628                 v = '&#160;';
38629             }
38630             if(this.fireEvent('beforepush', this, v) !== false){
38631                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38632                 this.fireEvent('push', this, v);
38633             }
38634         }
38635     },
38636
38637     // private
38638     deferFocus : function(){
38639         this.focus.defer(10, this);
38640     },
38641
38642     // doc'ed in Field
38643     focus : function(){
38644         if(this.win && !this.sourceEditMode){
38645             this.win.focus();
38646         }else{
38647             this.el.focus();
38648         }
38649     },
38650     
38651     assignDocWin: function()
38652     {
38653         var iframe = this.iframe;
38654         
38655          if(Roo.isIE){
38656             this.doc = iframe.contentWindow.document;
38657             this.win = iframe.contentWindow;
38658         } else {
38659             if (!Roo.get(this.frameId)) {
38660                 return;
38661             }
38662             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38663             this.win = Roo.get(this.frameId).dom.contentWindow;
38664         }
38665     },
38666     
38667     // private
38668     initEditor : function(){
38669         //console.log("INIT EDITOR");
38670         this.assignDocWin();
38671         
38672         
38673         
38674         this.doc.designMode="on";
38675         this.doc.open();
38676         this.doc.write(this.getDocMarkup());
38677         this.doc.close();
38678         
38679         var dbody = (this.doc.body || this.doc.documentElement);
38680         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38681         // this copies styles from the containing element into thsi one..
38682         // not sure why we need all of this..
38683         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38684         ss['background-attachment'] = 'fixed'; // w3c
38685         dbody.bgProperties = 'fixed'; // ie
38686         Roo.DomHelper.applyStyles(dbody, ss);
38687         Roo.EventManager.on(this.doc, {
38688             'mousedown': this.onEditorEvent,
38689             'dblclick': this.onEditorEvent,
38690             'click': this.onEditorEvent,
38691             'keyup': this.onEditorEvent,
38692             buffer:100,
38693             scope: this
38694         });
38695         if(Roo.isGecko){
38696             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38697         }
38698         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38699             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38700         }
38701         this.initialized = true;
38702
38703         this.fireEvent('initialize', this);
38704         this.pushValue();
38705     },
38706
38707     // private
38708     onDestroy : function(){
38709         
38710         
38711         
38712         if(this.rendered){
38713             
38714             for (var i =0; i < this.toolbars.length;i++) {
38715                 // fixme - ask toolbars for heights?
38716                 this.toolbars[i].onDestroy();
38717             }
38718             
38719             this.wrap.dom.innerHTML = '';
38720             this.wrap.remove();
38721         }
38722     },
38723
38724     // private
38725     onFirstFocus : function(){
38726         
38727         this.assignDocWin();
38728         
38729         
38730         this.activated = true;
38731         for (var i =0; i < this.toolbars.length;i++) {
38732             this.toolbars[i].onFirstFocus();
38733         }
38734        
38735         if(Roo.isGecko){ // prevent silly gecko errors
38736             this.win.focus();
38737             var s = this.win.getSelection();
38738             if(!s.focusNode || s.focusNode.nodeType != 3){
38739                 var r = s.getRangeAt(0);
38740                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38741                 r.collapse(true);
38742                 this.deferFocus();
38743             }
38744             try{
38745                 this.execCmd('useCSS', true);
38746                 this.execCmd('styleWithCSS', false);
38747             }catch(e){}
38748         }
38749         this.fireEvent('activate', this);
38750     },
38751
38752     // private
38753     adjustFont: function(btn){
38754         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38755         //if(Roo.isSafari){ // safari
38756         //    adjust *= 2;
38757        // }
38758         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38759         if(Roo.isSafari){ // safari
38760             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38761             v =  (v < 10) ? 10 : v;
38762             v =  (v > 48) ? 48 : v;
38763             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38764             
38765         }
38766         
38767         
38768         v = Math.max(1, v+adjust);
38769         
38770         this.execCmd('FontSize', v  );
38771     },
38772
38773     onEditorEvent : function(e){
38774         this.fireEvent('editorevent', this, e);
38775       //  this.updateToolbar();
38776         this.syncValue();
38777     },
38778
38779     insertTag : function(tg)
38780     {
38781         // could be a bit smarter... -> wrap the current selected tRoo..
38782         
38783         this.execCmd("formatblock",   tg);
38784         
38785     },
38786     
38787     insertText : function(txt)
38788     {
38789         
38790         
38791         range = this.createRange();
38792         range.deleteContents();
38793                //alert(Sender.getAttribute('label'));
38794                
38795         range.insertNode(this.doc.createTextNode(txt));
38796     } ,
38797     
38798     // private
38799     relayBtnCmd : function(btn){
38800         this.relayCmd(btn.cmd);
38801     },
38802
38803     /**
38804      * Executes a Midas editor command on the editor document and performs necessary focus and
38805      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38806      * @param {String} cmd The Midas command
38807      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38808      */
38809     relayCmd : function(cmd, value){
38810         this.win.focus();
38811         this.execCmd(cmd, value);
38812         this.fireEvent('editorevent', this);
38813         //this.updateToolbar();
38814         this.deferFocus();
38815     },
38816
38817     /**
38818      * Executes a Midas editor command directly on the editor document.
38819      * For visual commands, you should use {@link #relayCmd} instead.
38820      * <b>This should only be called after the editor is initialized.</b>
38821      * @param {String} cmd The Midas command
38822      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38823      */
38824     execCmd : function(cmd, value){
38825         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38826         this.syncValue();
38827     },
38828
38829    
38830     /**
38831      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38832      * to insert tRoo.
38833      * @param {String} text
38834      */
38835     insertAtCursor : function(text){
38836         if(!this.activated){
38837             return;
38838         }
38839         if(Roo.isIE){
38840             this.win.focus();
38841             var r = this.doc.selection.createRange();
38842             if(r){
38843                 r.collapse(true);
38844                 r.pasteHTML(text);
38845                 this.syncValue();
38846                 this.deferFocus();
38847             }
38848         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38849             this.win.focus();
38850             this.execCmd('InsertHTML', text);
38851             this.deferFocus();
38852         }
38853     },
38854  // private
38855     mozKeyPress : function(e){
38856         if(e.ctrlKey){
38857             var c = e.getCharCode(), cmd;
38858           
38859             if(c > 0){
38860                 c = String.fromCharCode(c).toLowerCase();
38861                 switch(c){
38862                     case 'b':
38863                         cmd = 'bold';
38864                     break;
38865                     case 'i':
38866                         cmd = 'italic';
38867                     break;
38868                     case 'u':
38869                         cmd = 'underline';
38870                     case 'v':
38871                         this.cleanUpPaste.defer(100, this);
38872                         return;
38873                     break;
38874                 }
38875                 if(cmd){
38876                     this.win.focus();
38877                     this.execCmd(cmd);
38878                     this.deferFocus();
38879                     e.preventDefault();
38880                 }
38881                 
38882             }
38883         }
38884     },
38885
38886     // private
38887     fixKeys : function(){ // load time branching for fastest keydown performance
38888         if(Roo.isIE){
38889             return function(e){
38890                 var k = e.getKey(), r;
38891                 if(k == e.TAB){
38892                     e.stopEvent();
38893                     r = this.doc.selection.createRange();
38894                     if(r){
38895                         r.collapse(true);
38896                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38897                         this.deferFocus();
38898                     }
38899                     return;
38900                 }
38901                 
38902                 if(k == e.ENTER){
38903                     r = this.doc.selection.createRange();
38904                     if(r){
38905                         var target = r.parentElement();
38906                         if(!target || target.tagName.toLowerCase() != 'li'){
38907                             e.stopEvent();
38908                             r.pasteHTML('<br />');
38909                             r.collapse(false);
38910                             r.select();
38911                         }
38912                     }
38913                 }
38914                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38915                     this.cleanUpPaste.defer(100, this);
38916                     return;
38917                 }
38918                 
38919                 
38920             };
38921         }else if(Roo.isOpera){
38922             return function(e){
38923                 var k = e.getKey();
38924                 if(k == e.TAB){
38925                     e.stopEvent();
38926                     this.win.focus();
38927                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38928                     this.deferFocus();
38929                 }
38930                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38931                     this.cleanUpPaste.defer(100, this);
38932                     return;
38933                 }
38934                 
38935             };
38936         }else if(Roo.isSafari){
38937             return function(e){
38938                 var k = e.getKey();
38939                 
38940                 if(k == e.TAB){
38941                     e.stopEvent();
38942                     this.execCmd('InsertText','\t');
38943                     this.deferFocus();
38944                     return;
38945                 }
38946                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38947                     this.cleanUpPaste.defer(100, this);
38948                     return;
38949                 }
38950                 
38951              };
38952         }
38953     }(),
38954     
38955     getAllAncestors: function()
38956     {
38957         var p = this.getSelectedNode();
38958         var a = [];
38959         if (!p) {
38960             a.push(p); // push blank onto stack..
38961             p = this.getParentElement();
38962         }
38963         
38964         
38965         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38966             a.push(p);
38967             p = p.parentNode;
38968         }
38969         a.push(this.doc.body);
38970         return a;
38971     },
38972     lastSel : false,
38973     lastSelNode : false,
38974     
38975     
38976     getSelection : function() 
38977     {
38978         this.assignDocWin();
38979         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38980     },
38981     
38982     getSelectedNode: function() 
38983     {
38984         // this may only work on Gecko!!!
38985         
38986         // should we cache this!!!!
38987         
38988         
38989         
38990          
38991         var range = this.createRange(this.getSelection());
38992         
38993         if (Roo.isIE) {
38994             var parent = range.parentElement();
38995             while (true) {
38996                 var testRange = range.duplicate();
38997                 testRange.moveToElementText(parent);
38998                 if (testRange.inRange(range)) {
38999                     break;
39000                 }
39001                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39002                     break;
39003                 }
39004                 parent = parent.parentElement;
39005             }
39006             return parent;
39007         }
39008         
39009         
39010         var ar = range.endContainer.childNodes;
39011         if (!ar.length) {
39012             ar = range.commonAncestorContainer.childNodes;
39013             //alert(ar.length);
39014         }
39015         var nodes = [];
39016         var other_nodes = [];
39017         var has_other_nodes = false;
39018         for (var i=0;i<ar.length;i++) {
39019             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39020                 continue;
39021             }
39022             // fullly contained node.
39023             
39024             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39025                 nodes.push(ar[i]);
39026                 continue;
39027             }
39028             
39029             // probably selected..
39030             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39031                 other_nodes.push(ar[i]);
39032                 continue;
39033             }
39034             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39035                 continue;
39036             }
39037             
39038             
39039             has_other_nodes = true;
39040         }
39041         if (!nodes.length && other_nodes.length) {
39042             nodes= other_nodes;
39043         }
39044         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39045             return false;
39046         }
39047         
39048         return nodes[0];
39049     },
39050     createRange: function(sel)
39051     {
39052         // this has strange effects when using with 
39053         // top toolbar - not sure if it's a great idea.
39054         //this.editor.contentWindow.focus();
39055         if (typeof sel != "undefined") {
39056             try {
39057                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39058             } catch(e) {
39059                 return this.doc.createRange();
39060             }
39061         } else {
39062             return this.doc.createRange();
39063         }
39064     },
39065     getParentElement: function()
39066     {
39067         
39068         this.assignDocWin();
39069         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39070         
39071         var range = this.createRange(sel);
39072          
39073         try {
39074             var p = range.commonAncestorContainer;
39075             while (p.nodeType == 3) { // text node
39076                 p = p.parentNode;
39077             }
39078             return p;
39079         } catch (e) {
39080             return null;
39081         }
39082     
39083     },
39084     
39085     
39086     
39087     // BC Hacks - cause I cant work out what i was trying to do..
39088     rangeIntersectsNode : function(range, node)
39089     {
39090         var nodeRange = node.ownerDocument.createRange();
39091         try {
39092             nodeRange.selectNode(node);
39093         }
39094         catch (e) {
39095             nodeRange.selectNodeContents(node);
39096         }
39097
39098         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39099                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39100     },
39101     rangeCompareNode : function(range, node) {
39102         var nodeRange = node.ownerDocument.createRange();
39103         try {
39104             nodeRange.selectNode(node);
39105         } catch (e) {
39106             nodeRange.selectNodeContents(node);
39107         }
39108         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39109         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39110
39111         if (nodeIsBefore && !nodeIsAfter)
39112             return 0;
39113         if (!nodeIsBefore && nodeIsAfter)
39114             return 1;
39115         if (nodeIsBefore && nodeIsAfter)
39116             return 2;
39117
39118         return 3;
39119     },
39120
39121     // private? - in a new class?
39122     cleanUpPaste :  function()
39123     {
39124         // cleans up the whole document..
39125       //  console.log('cleanuppaste');
39126         this.cleanUpChildren(this.doc.body)
39127         
39128         
39129     },
39130     cleanUpChildren : function (n)
39131     {
39132         if (!n.childNodes.length) {
39133             return;
39134         }
39135         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39136            this.cleanUpChild(n.childNodes[i]);
39137         }
39138     },
39139     
39140     
39141         
39142     
39143     cleanUpChild : function (node)
39144     {
39145         //console.log(node);
39146         if (node.nodeName == "#text") {
39147             // clean up silly Windows -- stuff?
39148             return; 
39149         }
39150         if (node.nodeName == "#comment") {
39151             node.parentNode.removeChild(node);
39152             // clean up silly Windows -- stuff?
39153             return; 
39154         }
39155         
39156         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39157             // remove node.
39158             node.parentNode.removeChild(node);
39159             return;
39160             
39161         }
39162         if (!node.attributes || !node.attributes.length) {
39163             this.cleanUpChildren(node);
39164             return;
39165         }
39166         
39167         function cleanAttr(n,v)
39168         {
39169             
39170             if (v.match(/^\./) || v.match(/^\//)) {
39171                 return;
39172             }
39173             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39174                 return;
39175             }
39176             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39177             node.removeAttribute(n);
39178             
39179         }
39180         
39181         function cleanStyle(n,v)
39182         {
39183             if (v.match(/expression/)) { //XSS?? should we even bother..
39184                 node.removeAttribute(n);
39185                 return;
39186             }
39187             
39188             
39189             var parts = v.split(/;/);
39190             Roo.each(parts, function(p) {
39191                 p = p.replace(/\s+/g,'');
39192                 if (!p.length) {
39193                     return;
39194                 }
39195                 var l = p.split(':').shift().replace(/\s+/g,'');
39196                 
39197                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39198                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39199                     node.removeAttribute(n);
39200                     return false;
39201                 }
39202             });
39203             
39204             
39205         }
39206         
39207         
39208         for (var i = node.attributes.length-1; i > -1 ; i--) {
39209             var a = node.attributes[i];
39210             //console.log(a);
39211             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39212                 node.removeAttribute(a.name);
39213                 return;
39214             }
39215             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39216                 cleanAttr(a.name,a.value); // fixme..
39217                 return;
39218             }
39219             if (a.name == 'style') {
39220                 cleanStyle(a.name,a.value);
39221             }
39222             /// clean up MS crap..
39223             if (a.name == 'class') {
39224                 if (a.value.match(/^Mso/)) {
39225                     node.className = '';
39226                 }
39227             }
39228             
39229             // style cleanup!?
39230             // class cleanup?
39231             
39232         }
39233         
39234         
39235         this.cleanUpChildren(node);
39236         
39237         
39238     }
39239     
39240     
39241     // hide stuff that is not compatible
39242     /**
39243      * @event blur
39244      * @hide
39245      */
39246     /**
39247      * @event change
39248      * @hide
39249      */
39250     /**
39251      * @event focus
39252      * @hide
39253      */
39254     /**
39255      * @event specialkey
39256      * @hide
39257      */
39258     /**
39259      * @cfg {String} fieldClass @hide
39260      */
39261     /**
39262      * @cfg {String} focusClass @hide
39263      */
39264     /**
39265      * @cfg {String} autoCreate @hide
39266      */
39267     /**
39268      * @cfg {String} inputType @hide
39269      */
39270     /**
39271      * @cfg {String} invalidClass @hide
39272      */
39273     /**
39274      * @cfg {String} invalidText @hide
39275      */
39276     /**
39277      * @cfg {String} msgFx @hide
39278      */
39279     /**
39280      * @cfg {String} validateOnBlur @hide
39281      */
39282 });
39283
39284 Roo.form.HtmlEditor.white = [
39285         'area', 'br', 'img', 'input', 'hr', 'wbr',
39286         
39287        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39288        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39289        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39290        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39291        'table',   'ul',         'xmp', 
39292        
39293        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39294       'thead',   'tr', 
39295      
39296       'dir', 'menu', 'ol', 'ul', 'dl',
39297        
39298       'embed',  'object'
39299 ];
39300
39301
39302 Roo.form.HtmlEditor.black = [
39303     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39304         'applet', // 
39305         'base',   'basefont', 'bgsound', 'blink',  'body', 
39306         'frame',  'frameset', 'head',    'html',   'ilayer', 
39307         'iframe', 'layer',  'link',     'meta',    'object',   
39308         'script', 'style' ,'title',  'xml' // clean later..
39309 ];
39310 Roo.form.HtmlEditor.clean = [
39311     'script', 'style', 'title', 'xml'
39312 ];
39313
39314 // attributes..
39315
39316 Roo.form.HtmlEditor.ablack = [
39317     'on'
39318 ];
39319     
39320 Roo.form.HtmlEditor.aclean = [ 
39321     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39322 ];
39323
39324 // protocols..
39325 Roo.form.HtmlEditor.pwhite= [
39326         'http',  'https',  'mailto'
39327 ];
39328
39329 Roo.form.HtmlEditor.cwhite= [
39330         'text-align',
39331         'font-size'
39332 ];
39333
39334 // <script type="text/javascript">
39335 /*
39336  * Based on
39337  * Ext JS Library 1.1.1
39338  * Copyright(c) 2006-2007, Ext JS, LLC.
39339  *  
39340  
39341  */
39342
39343 /**
39344  * @class Roo.form.HtmlEditorToolbar1
39345  * Basic Toolbar
39346  * 
39347  * Usage:
39348  *
39349  new Roo.form.HtmlEditor({
39350     ....
39351     toolbars : [
39352         new Roo.form.HtmlEditorToolbar1({
39353             disable : { fonts: 1 , format: 1, ..., ... , ...],
39354             btns : [ .... ]
39355         })
39356     }
39357      
39358  * 
39359  * @cfg {Object} disable List of elements to disable..
39360  * @cfg {Array} btns List of additional buttons.
39361  * 
39362  * 
39363  * NEEDS Extra CSS? 
39364  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39365  */
39366  
39367 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39368 {
39369     
39370     Roo.apply(this, config);
39371     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39372     // dont call parent... till later.
39373 }
39374
39375 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39376     
39377     tb: false,
39378     
39379     rendered: false,
39380     
39381     editor : false,
39382     /**
39383      * @cfg {Object} disable  List of toolbar elements to disable
39384          
39385      */
39386     disable : false,
39387       /**
39388      * @cfg {Array} fontFamilies An array of available font families
39389      */
39390     fontFamilies : [
39391         'Arial',
39392         'Courier New',
39393         'Tahoma',
39394         'Times New Roman',
39395         'Verdana'
39396     ],
39397     
39398     specialChars : [
39399            "&#169;",
39400           "&#174;",     
39401           "&#8482;",    
39402           "&#163;" ,    
39403          // "&#8212;",    
39404           "&#8230;",    
39405           "&#247;" ,    
39406         //  "&#225;" ,     ?? a acute?
39407            "&#8364;"    , //Euro
39408        //   "&#8220;"    ,
39409         //  "&#8221;"    ,
39410         //  "&#8226;"    ,
39411           "&#176;"  //   , // degrees
39412
39413          // "&#233;"     , // e ecute
39414          // "&#250;"     , // u ecute?
39415     ],
39416     inputElements : [ 
39417             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39418             "input:submit", "input:button", "select", "textarea", "label" ],
39419     formats : [
39420         ["p"] ,  
39421         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39422         ["pre"],[ "code"], 
39423         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39424     ],
39425      /**
39426      * @cfg {String} defaultFont default font to use.
39427      */
39428     defaultFont: 'tahoma',
39429    
39430     fontSelect : false,
39431     
39432     
39433     formatCombo : false,
39434     
39435     init : function(editor)
39436     {
39437         this.editor = editor;
39438         
39439         
39440         var fid = editor.frameId;
39441         var etb = this;
39442         function btn(id, toggle, handler){
39443             var xid = fid + '-'+ id ;
39444             return {
39445                 id : xid,
39446                 cmd : id,
39447                 cls : 'x-btn-icon x-edit-'+id,
39448                 enableToggle:toggle !== false,
39449                 scope: editor, // was editor...
39450                 handler:handler||editor.relayBtnCmd,
39451                 clickEvent:'mousedown',
39452                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39453                 tabIndex:-1
39454             };
39455         }
39456         
39457         
39458         
39459         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39460         this.tb = tb;
39461          // stop form submits
39462         tb.el.on('click', function(e){
39463             e.preventDefault(); // what does this do?
39464         });
39465
39466         if(!this.disable.font && !Roo.isSafari){
39467             /* why no safari for fonts
39468             editor.fontSelect = tb.el.createChild({
39469                 tag:'select',
39470                 tabIndex: -1,
39471                 cls:'x-font-select',
39472                 html: editor.createFontOptions()
39473             });
39474             editor.fontSelect.on('change', function(){
39475                 var font = editor.fontSelect.dom.value;
39476                 editor.relayCmd('fontname', font);
39477                 editor.deferFocus();
39478             }, editor);
39479             tb.add(
39480                 editor.fontSelect.dom,
39481                 '-'
39482             );
39483             */
39484         };
39485         if(!this.disable.formats){
39486             this.formatCombo = new Roo.form.ComboBox({
39487                 store: new Roo.data.SimpleStore({
39488                     id : 'tag',
39489                     fields: ['tag'],
39490                     data : this.formats // from states.js
39491                 }),
39492                 blockFocus : true,
39493                 //autoCreate : {tag: "div",  size: "20"},
39494                 displayField:'tag',
39495                 typeAhead: false,
39496                 mode: 'local',
39497                 editable : false,
39498                 triggerAction: 'all',
39499                 emptyText:'Add tag',
39500                 selectOnFocus:true,
39501                 width:135,
39502                 listeners : {
39503                     'select': function(c, r, i) {
39504                         editor.insertTag(r.get('tag'));
39505                         editor.focus();
39506                     }
39507                 }
39508
39509             });
39510             tb.addField(this.formatCombo);
39511             
39512         }
39513         
39514         if(!this.disable.format){
39515             tb.add(
39516                 btn('bold'),
39517                 btn('italic'),
39518                 btn('underline')
39519             );
39520         };
39521         if(!this.disable.fontSize){
39522             tb.add(
39523                 '-',
39524                 
39525                 
39526                 btn('increasefontsize', false, editor.adjustFont),
39527                 btn('decreasefontsize', false, editor.adjustFont)
39528             );
39529         };
39530         
39531         
39532         if(this.disable.colors){
39533             tb.add(
39534                 '-', {
39535                     id:editor.frameId +'-forecolor',
39536                     cls:'x-btn-icon x-edit-forecolor',
39537                     clickEvent:'mousedown',
39538                     tooltip: this.buttonTips['forecolor'] || undefined,
39539                     tabIndex:-1,
39540                     menu : new Roo.menu.ColorMenu({
39541                         allowReselect: true,
39542                         focus: Roo.emptyFn,
39543                         value:'000000',
39544                         plain:true,
39545                         selectHandler: function(cp, color){
39546                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39547                             editor.deferFocus();
39548                         },
39549                         scope: editor,
39550                         clickEvent:'mousedown'
39551                     })
39552                 }, {
39553                     id:editor.frameId +'backcolor',
39554                     cls:'x-btn-icon x-edit-backcolor',
39555                     clickEvent:'mousedown',
39556                     tooltip: this.buttonTips['backcolor'] || undefined,
39557                     tabIndex:-1,
39558                     menu : new Roo.menu.ColorMenu({
39559                         focus: Roo.emptyFn,
39560                         value:'FFFFFF',
39561                         plain:true,
39562                         allowReselect: true,
39563                         selectHandler: function(cp, color){
39564                             if(Roo.isGecko){
39565                                 editor.execCmd('useCSS', false);
39566                                 editor.execCmd('hilitecolor', color);
39567                                 editor.execCmd('useCSS', true);
39568                                 editor.deferFocus();
39569                             }else{
39570                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39571                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39572                                 editor.deferFocus();
39573                             }
39574                         },
39575                         scope:editor,
39576                         clickEvent:'mousedown'
39577                     })
39578                 }
39579             );
39580         };
39581         // now add all the items...
39582         
39583
39584         if(!this.disable.alignments){
39585             tb.add(
39586                 '-',
39587                 btn('justifyleft'),
39588                 btn('justifycenter'),
39589                 btn('justifyright')
39590             );
39591         };
39592
39593         //if(!Roo.isSafari){
39594             if(!this.disable.links){
39595                 tb.add(
39596                     '-',
39597                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39598                 );
39599             };
39600
39601             if(!this.disable.lists){
39602                 tb.add(
39603                     '-',
39604                     btn('insertorderedlist'),
39605                     btn('insertunorderedlist')
39606                 );
39607             }
39608             if(!this.disable.sourceEdit){
39609                 tb.add(
39610                     '-',
39611                     btn('sourceedit', true, function(btn){
39612                         this.toggleSourceEdit(btn.pressed);
39613                     })
39614                 );
39615             }
39616         //}
39617         
39618         var smenu = { };
39619         // special menu.. - needs to be tidied up..
39620         if (!this.disable.special) {
39621             smenu = {
39622                 text: "&#169;",
39623                 cls: 'x-edit-none',
39624                 menu : {
39625                     items : []
39626                    }
39627             };
39628             for (var i =0; i < this.specialChars.length; i++) {
39629                 smenu.menu.items.push({
39630                     
39631                     html: this.specialChars[i],
39632                     handler: function(a,b) {
39633                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39634                         
39635                     },
39636                     tabIndex:-1
39637                 });
39638             }
39639             
39640             
39641             tb.add(smenu);
39642             
39643             
39644         }
39645         if (this.btns) {
39646             for(var i =0; i< this.btns.length;i++) {
39647                 var b = this.btns[i];
39648                 b.cls =  'x-edit-none';
39649                 b.scope = editor;
39650                 tb.add(b);
39651             }
39652         
39653         }
39654         
39655         
39656         
39657         // disable everything...
39658         
39659         this.tb.items.each(function(item){
39660            if(item.id != editor.frameId+ '-sourceedit'){
39661                 item.disable();
39662             }
39663         });
39664         this.rendered = true;
39665         
39666         // the all the btns;
39667         editor.on('editorevent', this.updateToolbar, this);
39668         // other toolbars need to implement this..
39669         //editor.on('editmodechange', this.updateToolbar, this);
39670     },
39671     
39672     
39673     
39674     /**
39675      * Protected method that will not generally be called directly. It triggers
39676      * a toolbar update by reading the markup state of the current selection in the editor.
39677      */
39678     updateToolbar: function(){
39679
39680         if(!this.editor.activated){
39681             this.editor.onFirstFocus();
39682             return;
39683         }
39684
39685         var btns = this.tb.items.map, 
39686             doc = this.editor.doc,
39687             frameId = this.editor.frameId;
39688
39689         if(!this.disable.font && !Roo.isSafari){
39690             /*
39691             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39692             if(name != this.fontSelect.dom.value){
39693                 this.fontSelect.dom.value = name;
39694             }
39695             */
39696         }
39697         if(!this.disable.format){
39698             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39699             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39700             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39701         }
39702         if(!this.disable.alignments){
39703             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39704             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39705             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39706         }
39707         if(!Roo.isSafari && !this.disable.lists){
39708             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39709             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39710         }
39711         
39712         var ans = this.editor.getAllAncestors();
39713         if (this.formatCombo) {
39714             
39715             
39716             var store = this.formatCombo.store;
39717             this.formatCombo.setValue("");
39718             for (var i =0; i < ans.length;i++) {
39719                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39720                     // select it..
39721                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39722                     break;
39723                 }
39724             }
39725         }
39726         
39727         
39728         
39729         // hides menus... - so this cant be on a menu...
39730         Roo.menu.MenuMgr.hideAll();
39731
39732         //this.editorsyncValue();
39733     },
39734    
39735     
39736     createFontOptions : function(){
39737         var buf = [], fs = this.fontFamilies, ff, lc;
39738         for(var i = 0, len = fs.length; i< len; i++){
39739             ff = fs[i];
39740             lc = ff.toLowerCase();
39741             buf.push(
39742                 '<option value="',lc,'" style="font-family:',ff,';"',
39743                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39744                     ff,
39745                 '</option>'
39746             );
39747         }
39748         return buf.join('');
39749     },
39750     
39751     toggleSourceEdit : function(sourceEditMode){
39752         if(sourceEditMode === undefined){
39753             sourceEditMode = !this.sourceEditMode;
39754         }
39755         this.sourceEditMode = sourceEditMode === true;
39756         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39757         // just toggle the button?
39758         if(btn.pressed !== this.editor.sourceEditMode){
39759             btn.toggle(this.editor.sourceEditMode);
39760             return;
39761         }
39762         
39763         if(this.sourceEditMode){
39764             this.tb.items.each(function(item){
39765                 if(item.cmd != 'sourceedit'){
39766                     item.disable();
39767                 }
39768             });
39769           
39770         }else{
39771             if(this.initialized){
39772                 this.tb.items.each(function(item){
39773                     item.enable();
39774                 });
39775             }
39776             
39777         }
39778         // tell the editor that it's been pressed..
39779         this.editor.toggleSourceEdit(sourceEditMode);
39780        
39781     },
39782      /**
39783      * Object collection of toolbar tooltips for the buttons in the editor. The key
39784      * is the command id associated with that button and the value is a valid QuickTips object.
39785      * For example:
39786 <pre><code>
39787 {
39788     bold : {
39789         title: 'Bold (Ctrl+B)',
39790         text: 'Make the selected text bold.',
39791         cls: 'x-html-editor-tip'
39792     },
39793     italic : {
39794         title: 'Italic (Ctrl+I)',
39795         text: 'Make the selected text italic.',
39796         cls: 'x-html-editor-tip'
39797     },
39798     ...
39799 </code></pre>
39800     * @type Object
39801      */
39802     buttonTips : {
39803         bold : {
39804             title: 'Bold (Ctrl+B)',
39805             text: 'Make the selected text bold.',
39806             cls: 'x-html-editor-tip'
39807         },
39808         italic : {
39809             title: 'Italic (Ctrl+I)',
39810             text: 'Make the selected text italic.',
39811             cls: 'x-html-editor-tip'
39812         },
39813         underline : {
39814             title: 'Underline (Ctrl+U)',
39815             text: 'Underline the selected text.',
39816             cls: 'x-html-editor-tip'
39817         },
39818         increasefontsize : {
39819             title: 'Grow Text',
39820             text: 'Increase the font size.',
39821             cls: 'x-html-editor-tip'
39822         },
39823         decreasefontsize : {
39824             title: 'Shrink Text',
39825             text: 'Decrease the font size.',
39826             cls: 'x-html-editor-tip'
39827         },
39828         backcolor : {
39829             title: 'Text Highlight Color',
39830             text: 'Change the background color of the selected text.',
39831             cls: 'x-html-editor-tip'
39832         },
39833         forecolor : {
39834             title: 'Font Color',
39835             text: 'Change the color of the selected text.',
39836             cls: 'x-html-editor-tip'
39837         },
39838         justifyleft : {
39839             title: 'Align Text Left',
39840             text: 'Align text to the left.',
39841             cls: 'x-html-editor-tip'
39842         },
39843         justifycenter : {
39844             title: 'Center Text',
39845             text: 'Center text in the editor.',
39846             cls: 'x-html-editor-tip'
39847         },
39848         justifyright : {
39849             title: 'Align Text Right',
39850             text: 'Align text to the right.',
39851             cls: 'x-html-editor-tip'
39852         },
39853         insertunorderedlist : {
39854             title: 'Bullet List',
39855             text: 'Start a bulleted list.',
39856             cls: 'x-html-editor-tip'
39857         },
39858         insertorderedlist : {
39859             title: 'Numbered List',
39860             text: 'Start a numbered list.',
39861             cls: 'x-html-editor-tip'
39862         },
39863         createlink : {
39864             title: 'Hyperlink',
39865             text: 'Make the selected text a hyperlink.',
39866             cls: 'x-html-editor-tip'
39867         },
39868         sourceedit : {
39869             title: 'Source Edit',
39870             text: 'Switch to source editing mode.',
39871             cls: 'x-html-editor-tip'
39872         }
39873     },
39874     // private
39875     onDestroy : function(){
39876         if(this.rendered){
39877             
39878             this.tb.items.each(function(item){
39879                 if(item.menu){
39880                     item.menu.removeAll();
39881                     if(item.menu.el){
39882                         item.menu.el.destroy();
39883                     }
39884                 }
39885                 item.destroy();
39886             });
39887              
39888         }
39889     },
39890     onFirstFocus: function() {
39891         this.tb.items.each(function(item){
39892            item.enable();
39893         });
39894     }
39895 });
39896
39897
39898
39899
39900 // <script type="text/javascript">
39901 /*
39902  * Based on
39903  * Ext JS Library 1.1.1
39904  * Copyright(c) 2006-2007, Ext JS, LLC.
39905  *  
39906  
39907  */
39908
39909  
39910 /**
39911  * @class Roo.form.HtmlEditor.ToolbarContext
39912  * Context Toolbar
39913  * 
39914  * Usage:
39915  *
39916  new Roo.form.HtmlEditor({
39917     ....
39918     toolbars : [
39919         new Roo.form.HtmlEditor.ToolbarStandard(),
39920         new Roo.form.HtmlEditor.ToolbarContext()
39921         })
39922     }
39923      
39924  * 
39925  * @config : {Object} disable List of elements to disable.. (not done yet.)
39926  * 
39927  * 
39928  */
39929
39930 Roo.form.HtmlEditor.ToolbarContext = function(config)
39931 {
39932     
39933     Roo.apply(this, config);
39934     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39935     // dont call parent... till later.
39936 }
39937 Roo.form.HtmlEditor.ToolbarContext.types = {
39938     'IMG' : {
39939         width : {
39940             title: "Width",
39941             width: 40
39942         },
39943         height:  {
39944             title: "Height",
39945             width: 40
39946         },
39947         align: {
39948             title: "Align",
39949             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39950             width : 80
39951             
39952         },
39953         border: {
39954             title: "Border",
39955             width: 40
39956         },
39957         alt: {
39958             title: "Alt",
39959             width: 120
39960         },
39961         src : {
39962             title: "Src",
39963             width: 220
39964         }
39965         
39966     },
39967     'A' : {
39968         name : {
39969             title: "Name",
39970             width: 50
39971         },
39972         href:  {
39973             title: "Href",
39974             width: 220
39975         } // border?
39976         
39977     },
39978     'TABLE' : {
39979         rows : {
39980             title: "Rows",
39981             width: 20
39982         },
39983         cols : {
39984             title: "Cols",
39985             width: 20
39986         },
39987         width : {
39988             title: "Width",
39989             width: 40
39990         },
39991         height : {
39992             title: "Height",
39993             width: 40
39994         },
39995         border : {
39996             title: "Border",
39997             width: 20
39998         }
39999     },
40000     'TD' : {
40001         width : {
40002             title: "Width",
40003             width: 40
40004         },
40005         height : {
40006             title: "Height",
40007             width: 40
40008         },   
40009         align: {
40010             title: "Align",
40011             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40012             width: 40
40013         },
40014         valign: {
40015             title: "Valign",
40016             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40017             width: 40
40018         },
40019         colspan: {
40020             title: "Colspan",
40021             width: 20
40022             
40023         }
40024     },
40025     'INPUT' : {
40026         name : {
40027             title: "name",
40028             width: 120
40029         },
40030         value : {
40031             title: "Value",
40032             width: 120
40033         },
40034         width : {
40035             title: "Width",
40036             width: 40
40037         }
40038     },
40039     'LABEL' : {
40040         'for' : {
40041             title: "For",
40042             width: 120
40043         }
40044     },
40045     'TEXTAREA' : {
40046           name : {
40047             title: "name",
40048             width: 120
40049         },
40050         rows : {
40051             title: "Rows",
40052             width: 20
40053         },
40054         cols : {
40055             title: "Cols",
40056             width: 20
40057         }
40058     },
40059     'SELECT' : {
40060         name : {
40061             title: "name",
40062             width: 120
40063         },
40064         selectoptions : {
40065             title: "Options",
40066             width: 200
40067         }
40068     },
40069     'BODY' : {
40070         title : {
40071             title: "title",
40072             width: 120,
40073             disabled : true
40074         }
40075     }
40076 };
40077
40078
40079
40080 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40081     
40082     tb: false,
40083     
40084     rendered: false,
40085     
40086     editor : false,
40087     /**
40088      * @cfg {Object} disable  List of toolbar elements to disable
40089          
40090      */
40091     disable : false,
40092     
40093     
40094     
40095     toolbars : false,
40096     
40097     init : function(editor)
40098     {
40099         this.editor = editor;
40100         
40101         
40102         var fid = editor.frameId;
40103         var etb = this;
40104         function btn(id, toggle, handler){
40105             var xid = fid + '-'+ id ;
40106             return {
40107                 id : xid,
40108                 cmd : id,
40109                 cls : 'x-btn-icon x-edit-'+id,
40110                 enableToggle:toggle !== false,
40111                 scope: editor, // was editor...
40112                 handler:handler||editor.relayBtnCmd,
40113                 clickEvent:'mousedown',
40114                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40115                 tabIndex:-1
40116             };
40117         }
40118         // create a new element.
40119         var wdiv = editor.wrap.createChild({
40120                 tag: 'div'
40121             }, editor.wrap.dom.firstChild.nextSibling, true);
40122         
40123         // can we do this more than once??
40124         
40125          // stop form submits
40126       
40127  
40128         // disable everything...
40129         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40130         this.toolbars = {};
40131            
40132         for (var i in  ty) {
40133           
40134             this.toolbars[i] = this.buildToolbar(ty[i],i);
40135         }
40136         this.tb = this.toolbars.BODY;
40137         this.tb.el.show();
40138         
40139          
40140         this.rendered = true;
40141         
40142         // the all the btns;
40143         editor.on('editorevent', this.updateToolbar, this);
40144         // other toolbars need to implement this..
40145         //editor.on('editmodechange', this.updateToolbar, this);
40146     },
40147     
40148     
40149     
40150     /**
40151      * Protected method that will not generally be called directly. It triggers
40152      * a toolbar update by reading the markup state of the current selection in the editor.
40153      */
40154     updateToolbar: function(){
40155
40156         if(!this.editor.activated){
40157             this.editor.onFirstFocus();
40158             return;
40159         }
40160
40161         
40162         var ans = this.editor.getAllAncestors();
40163         
40164         // pick
40165         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40166         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40167         sel = sel ? sel : this.editor.doc.body;
40168         sel = sel.tagName.length ? sel : this.editor.doc.body;
40169         var tn = sel.tagName.toUpperCase();
40170         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40171         tn = sel.tagName.toUpperCase();
40172         if (this.tb.name  == tn) {
40173             return; // no change
40174         }
40175         this.tb.el.hide();
40176         ///console.log("show: " + tn);
40177         this.tb =  this.toolbars[tn];
40178         this.tb.el.show();
40179         this.tb.fields.each(function(e) {
40180             e.setValue(sel.getAttribute(e.name));
40181         });
40182         this.tb.selectedNode = sel;
40183         
40184         
40185         Roo.menu.MenuMgr.hideAll();
40186
40187         //this.editorsyncValue();
40188     },
40189    
40190        
40191     // private
40192     onDestroy : function(){
40193         if(this.rendered){
40194             
40195             this.tb.items.each(function(item){
40196                 if(item.menu){
40197                     item.menu.removeAll();
40198                     if(item.menu.el){
40199                         item.menu.el.destroy();
40200                     }
40201                 }
40202                 item.destroy();
40203             });
40204              
40205         }
40206     },
40207     onFirstFocus: function() {
40208         // need to do this for all the toolbars..
40209         this.tb.items.each(function(item){
40210            item.enable();
40211         });
40212     },
40213     buildToolbar: function(tlist, nm)
40214     {
40215         var editor = this.editor;
40216          // create a new element.
40217         var wdiv = editor.wrap.createChild({
40218                 tag: 'div'
40219             }, editor.wrap.dom.firstChild.nextSibling, true);
40220         
40221        
40222         var tb = new Roo.Toolbar(wdiv);
40223         tb.add(nm+ ":&nbsp;");
40224         for (var i in tlist) {
40225             var item = tlist[i];
40226             tb.add(item.title + ":&nbsp;");
40227             if (item.opts) {
40228                 // fixme
40229                 
40230               
40231                 tb.addField( new Roo.form.ComboBox({
40232                     store: new Roo.data.SimpleStore({
40233                         id : 'val',
40234                         fields: ['val'],
40235                         data : item.opts // from states.js
40236                     }),
40237                     name : i,
40238                     displayField:'val',
40239                     typeAhead: false,
40240                     mode: 'local',
40241                     editable : false,
40242                     triggerAction: 'all',
40243                     emptyText:'Select',
40244                     selectOnFocus:true,
40245                     width: item.width ? item.width  : 130,
40246                     listeners : {
40247                         'select': function(c, r, i) {
40248                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40249                         }
40250                     }
40251
40252                 }));
40253                 continue;
40254                     
40255                 
40256                 
40257                 
40258                 
40259                 tb.addField( new Roo.form.TextField({
40260                     name: i,
40261                     width: 100,
40262                     //allowBlank:false,
40263                     value: ''
40264                 }));
40265                 continue;
40266             }
40267             tb.addField( new Roo.form.TextField({
40268                 name: i,
40269                 width: item.width,
40270                 //allowBlank:true,
40271                 value: '',
40272                 listeners: {
40273                     'change' : function(f, nv, ov) {
40274                         tb.selectedNode.setAttribute(f.name, nv);
40275                     }
40276                 }
40277             }));
40278              
40279         }
40280         tb.el.on('click', function(e){
40281             e.preventDefault(); // what does this do?
40282         });
40283         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40284         tb.el.hide();
40285         tb.name = nm;
40286         // dont need to disable them... as they will get hidden
40287         return tb;
40288          
40289         
40290     }
40291     
40292     
40293     
40294     
40295 });
40296
40297
40298
40299
40300
40301 /*
40302  * Based on:
40303  * Ext JS Library 1.1.1
40304  * Copyright(c) 2006-2007, Ext JS, LLC.
40305  *
40306  * Originally Released Under LGPL - original licence link has changed is not relivant.
40307  *
40308  * Fork - LGPL
40309  * <script type="text/javascript">
40310  */
40311  
40312 /**
40313  * @class Roo.form.BasicForm
40314  * @extends Roo.util.Observable
40315  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40316  * @constructor
40317  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40318  * @param {Object} config Configuration options
40319  */
40320 Roo.form.BasicForm = function(el, config){
40321     this.allItems = [];
40322     this.childForms = [];
40323     Roo.apply(this, config);
40324     /*
40325      * The Roo.form.Field items in this form.
40326      * @type MixedCollection
40327      */
40328      
40329      
40330     this.items = new Roo.util.MixedCollection(false, function(o){
40331         return o.id || (o.id = Roo.id());
40332     });
40333     this.addEvents({
40334         /**
40335          * @event beforeaction
40336          * Fires before any action is performed. Return false to cancel the action.
40337          * @param {Form} this
40338          * @param {Action} action The action to be performed
40339          */
40340         beforeaction: true,
40341         /**
40342          * @event actionfailed
40343          * Fires when an action fails.
40344          * @param {Form} this
40345          * @param {Action} action The action that failed
40346          */
40347         actionfailed : true,
40348         /**
40349          * @event actioncomplete
40350          * Fires when an action is completed.
40351          * @param {Form} this
40352          * @param {Action} action The action that completed
40353          */
40354         actioncomplete : true
40355     });
40356     if(el){
40357         this.initEl(el);
40358     }
40359     Roo.form.BasicForm.superclass.constructor.call(this);
40360 };
40361
40362 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40363     /**
40364      * @cfg {String} method
40365      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40366      */
40367     /**
40368      * @cfg {DataReader} reader
40369      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40370      * This is optional as there is built-in support for processing JSON.
40371      */
40372     /**
40373      * @cfg {DataReader} errorReader
40374      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40375      * This is completely optional as there is built-in support for processing JSON.
40376      */
40377     /**
40378      * @cfg {String} url
40379      * The URL to use for form actions if one isn't supplied in the action options.
40380      */
40381     /**
40382      * @cfg {Boolean} fileUpload
40383      * Set to true if this form is a file upload.
40384      */
40385      
40386     /**
40387      * @cfg {Object} baseParams
40388      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40389      */
40390      /**
40391      
40392     /**
40393      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40394      */
40395     timeout: 30,
40396
40397     // private
40398     activeAction : null,
40399
40400     /**
40401      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40402      * or setValues() data instead of when the form was first created.
40403      */
40404     trackResetOnLoad : false,
40405     
40406     
40407     /**
40408      * childForms - used for multi-tab forms
40409      * @type {Array}
40410      */
40411     childForms : false,
40412     
40413     /**
40414      * allItems - full list of fields.
40415      * @type {Array}
40416      */
40417     allItems : false,
40418     
40419     /**
40420      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40421      * element by passing it or its id or mask the form itself by passing in true.
40422      * @type Mixed
40423      */
40424     waitMsgTarget : false,
40425
40426     // private
40427     initEl : function(el){
40428         this.el = Roo.get(el);
40429         this.id = this.el.id || Roo.id();
40430         this.el.on('submit', this.onSubmit, this);
40431         this.el.addClass('x-form');
40432     },
40433
40434     // private
40435     onSubmit : function(e){
40436         e.stopEvent();
40437     },
40438
40439     /**
40440      * Returns true if client-side validation on the form is successful.
40441      * @return Boolean
40442      */
40443     isValid : function(){
40444         var valid = true;
40445         this.items.each(function(f){
40446            if(!f.validate()){
40447                valid = false;
40448            }
40449         });
40450         return valid;
40451     },
40452
40453     /**
40454      * Returns true if any fields in this form have changed since their original load.
40455      * @return Boolean
40456      */
40457     isDirty : function(){
40458         var dirty = false;
40459         this.items.each(function(f){
40460            if(f.isDirty()){
40461                dirty = true;
40462                return false;
40463            }
40464         });
40465         return dirty;
40466     },
40467
40468     /**
40469      * Performs a predefined action (submit or load) or custom actions you define on this form.
40470      * @param {String} actionName The name of the action type
40471      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40472      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40473      * accept other config options):
40474      * <pre>
40475 Property          Type             Description
40476 ----------------  ---------------  ----------------------------------------------------------------------------------
40477 url               String           The url for the action (defaults to the form's url)
40478 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40479 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40480 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40481                                    validate the form on the client (defaults to false)
40482      * </pre>
40483      * @return {BasicForm} this
40484      */
40485     doAction : function(action, options){
40486         if(typeof action == 'string'){
40487             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40488         }
40489         if(this.fireEvent('beforeaction', this, action) !== false){
40490             this.beforeAction(action);
40491             action.run.defer(100, action);
40492         }
40493         return this;
40494     },
40495
40496     /**
40497      * Shortcut to do a submit action.
40498      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40499      * @return {BasicForm} this
40500      */
40501     submit : function(options){
40502         this.doAction('submit', options);
40503         return this;
40504     },
40505
40506     /**
40507      * Shortcut to do a load action.
40508      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40509      * @return {BasicForm} this
40510      */
40511     load : function(options){
40512         this.doAction('load', options);
40513         return this;
40514     },
40515
40516     /**
40517      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40518      * @param {Record} record The record to edit
40519      * @return {BasicForm} this
40520      */
40521     updateRecord : function(record){
40522         record.beginEdit();
40523         var fs = record.fields;
40524         fs.each(function(f){
40525             var field = this.findField(f.name);
40526             if(field){
40527                 record.set(f.name, field.getValue());
40528             }
40529         }, this);
40530         record.endEdit();
40531         return this;
40532     },
40533
40534     /**
40535      * Loads an Roo.data.Record into this form.
40536      * @param {Record} record The record to load
40537      * @return {BasicForm} this
40538      */
40539     loadRecord : function(record){
40540         this.setValues(record.data);
40541         return this;
40542     },
40543
40544     // private
40545     beforeAction : function(action){
40546         var o = action.options;
40547         
40548        
40549         if(this.waitMsgTarget === true){
40550             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40551         }else if(this.waitMsgTarget){
40552             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40553             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40554         }else {
40555             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40556         }
40557          
40558     },
40559
40560     // private
40561     afterAction : function(action, success){
40562         this.activeAction = null;
40563         var o = action.options;
40564         
40565         if(this.waitMsgTarget === true){
40566             this.el.unmask();
40567         }else if(this.waitMsgTarget){
40568             this.waitMsgTarget.unmask();
40569         }else{
40570             Roo.MessageBox.updateProgress(1);
40571             Roo.MessageBox.hide();
40572         }
40573          
40574         if(success){
40575             if(o.reset){
40576                 this.reset();
40577             }
40578             Roo.callback(o.success, o.scope, [this, action]);
40579             this.fireEvent('actioncomplete', this, action);
40580             
40581         }else{
40582             Roo.callback(o.failure, o.scope, [this, action]);
40583             // show an error message if no failed handler is set..
40584             if (!this.hasListener('actionfailed')) {
40585                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40586             }
40587             
40588             this.fireEvent('actionfailed', this, action);
40589         }
40590         
40591     },
40592
40593     /**
40594      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40595      * @param {String} id The value to search for
40596      * @return Field
40597      */
40598     findField : function(id){
40599         var field = this.items.get(id);
40600         if(!field){
40601             this.items.each(function(f){
40602                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40603                     field = f;
40604                     return false;
40605                 }
40606             });
40607         }
40608         return field || null;
40609     },
40610
40611     /**
40612      * Add a secondary form to this one, 
40613      * Used to provide tabbed forms. One form is primary, with hidden values 
40614      * which mirror the elements from the other forms.
40615      * 
40616      * @param {Roo.form.Form} form to add.
40617      * 
40618      */
40619     addForm : function(form)
40620     {
40621        
40622         if (this.childForms.indexOf(form) > -1) {
40623             // already added..
40624             return;
40625         }
40626         this.childForms.push(form);
40627         var n = '';
40628         Roo.each(form.allItems, function (fe) {
40629             
40630             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40631             if (this.findField(n)) { // already added..
40632                 return;
40633             }
40634             var add = new Roo.form.Hidden({
40635                 name : n
40636             });
40637             add.render(this.el);
40638             
40639             this.add( add );
40640         }, this);
40641         
40642     },
40643     /**
40644      * Mark fields in this form invalid in bulk.
40645      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40646      * @return {BasicForm} this
40647      */
40648     markInvalid : function(errors){
40649         if(errors instanceof Array){
40650             for(var i = 0, len = errors.length; i < len; i++){
40651                 var fieldError = errors[i];
40652                 var f = this.findField(fieldError.id);
40653                 if(f){
40654                     f.markInvalid(fieldError.msg);
40655                 }
40656             }
40657         }else{
40658             var field, id;
40659             for(id in errors){
40660                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40661                     field.markInvalid(errors[id]);
40662                 }
40663             }
40664         }
40665         Roo.each(this.childForms || [], function (f) {
40666             f.markInvalid(errors);
40667         });
40668         
40669         return this;
40670     },
40671
40672     /**
40673      * Set values for fields in this form in bulk.
40674      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40675      * @return {BasicForm} this
40676      */
40677     setValues : function(values){
40678         if(values instanceof Array){ // array of objects
40679             for(var i = 0, len = values.length; i < len; i++){
40680                 var v = values[i];
40681                 var f = this.findField(v.id);
40682                 if(f){
40683                     f.setValue(v.value);
40684                     if(this.trackResetOnLoad){
40685                         f.originalValue = f.getValue();
40686                     }
40687                 }
40688             }
40689         }else{ // object hash
40690             var field, id;
40691             for(id in values){
40692                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40693                     
40694                     if (field.setFromData && 
40695                         field.valueField && 
40696                         field.displayField &&
40697                         // combos' with local stores can 
40698                         // be queried via setValue()
40699                         // to set their value..
40700                         (field.store && !field.store.isLocal)
40701                         ) {
40702                         // it's a combo
40703                         var sd = { };
40704                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40705                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40706                         field.setFromData(sd);
40707                         
40708                     } else {
40709                         field.setValue(values[id]);
40710                     }
40711                     
40712                     
40713                     if(this.trackResetOnLoad){
40714                         field.originalValue = field.getValue();
40715                     }
40716                 }
40717             }
40718         }
40719          
40720         Roo.each(this.childForms || [], function (f) {
40721             f.setValues(values);
40722         });
40723                 
40724         return this;
40725     },
40726
40727     /**
40728      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40729      * they are returned as an array.
40730      * @param {Boolean} asString
40731      * @return {Object}
40732      */
40733     getValues : function(asString){
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         
40742         
40743         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40744         if(asString === true){
40745             return fs;
40746         }
40747         return Roo.urlDecode(fs);
40748     },
40749     
40750     /**
40751      * Returns the fields in this form as an object with key/value pairs. 
40752      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40753      * @return {Object}
40754      */
40755     getFieldValues : function()
40756     {
40757         if (this.childForms) {
40758             // copy values from the child forms
40759             Roo.each(this.childForms, function (f) {
40760                 this.setValues(f.getValues());
40761             }, this);
40762         }
40763         
40764         var ret = {};
40765         this.items.each(function(f){
40766             if (!f.getName()) {
40767                 return;
40768             }
40769             var v = f.getValue();
40770             if ((typeof(v) == 'object') && f.getRawValue) {
40771                 v = f.getRawValue() ; // dates..
40772             }
40773             ret[f.getName()] = v;
40774         });
40775         
40776         return ret;
40777     },
40778
40779     /**
40780      * Clears all invalid messages in this form.
40781      * @return {BasicForm} this
40782      */
40783     clearInvalid : function(){
40784         this.items.each(function(f){
40785            f.clearInvalid();
40786         });
40787         
40788         Roo.each(this.childForms || [], function (f) {
40789             f.clearInvalid();
40790         });
40791         
40792         
40793         return this;
40794     },
40795
40796     /**
40797      * Resets this form.
40798      * @return {BasicForm} this
40799      */
40800     reset : function(){
40801         this.items.each(function(f){
40802             f.reset();
40803         });
40804         
40805         Roo.each(this.childForms || [], function (f) {
40806             f.reset();
40807         });
40808        
40809         
40810         return this;
40811     },
40812
40813     /**
40814      * Add Roo.form components to this form.
40815      * @param {Field} field1
40816      * @param {Field} field2 (optional)
40817      * @param {Field} etc (optional)
40818      * @return {BasicForm} this
40819      */
40820     add : function(){
40821         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40822         return this;
40823     },
40824
40825
40826     /**
40827      * Removes a field from the items collection (does NOT remove its markup).
40828      * @param {Field} field
40829      * @return {BasicForm} this
40830      */
40831     remove : function(field){
40832         this.items.remove(field);
40833         return this;
40834     },
40835
40836     /**
40837      * Looks at the fields in this form, checks them for an id attribute,
40838      * and calls applyTo on the existing dom element with that id.
40839      * @return {BasicForm} this
40840      */
40841     render : function(){
40842         this.items.each(function(f){
40843             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40844                 f.applyTo(f.id);
40845             }
40846         });
40847         return this;
40848     },
40849
40850     /**
40851      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40852      * @param {Object} values
40853      * @return {BasicForm} this
40854      */
40855     applyToFields : function(o){
40856         this.items.each(function(f){
40857            Roo.apply(f, o);
40858         });
40859         return this;
40860     },
40861
40862     /**
40863      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40864      * @param {Object} values
40865      * @return {BasicForm} this
40866      */
40867     applyIfToFields : function(o){
40868         this.items.each(function(f){
40869            Roo.applyIf(f, o);
40870         });
40871         return this;
40872     }
40873 });
40874
40875 // back compat
40876 Roo.BasicForm = Roo.form.BasicForm;/*
40877  * Based on:
40878  * Ext JS Library 1.1.1
40879  * Copyright(c) 2006-2007, Ext JS, LLC.
40880  *
40881  * Originally Released Under LGPL - original licence link has changed is not relivant.
40882  *
40883  * Fork - LGPL
40884  * <script type="text/javascript">
40885  */
40886
40887 /**
40888  * @class Roo.form.Form
40889  * @extends Roo.form.BasicForm
40890  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40891  * @constructor
40892  * @param {Object} config Configuration options
40893  */
40894 Roo.form.Form = function(config){
40895     var xitems =  [];
40896     if (config.items) {
40897         xitems = config.items;
40898         delete config.items;
40899     }
40900    
40901     
40902     Roo.form.Form.superclass.constructor.call(this, null, config);
40903     this.url = this.url || this.action;
40904     if(!this.root){
40905         this.root = new Roo.form.Layout(Roo.applyIf({
40906             id: Roo.id()
40907         }, config));
40908     }
40909     this.active = this.root;
40910     /**
40911      * Array of all the buttons that have been added to this form via {@link addButton}
40912      * @type Array
40913      */
40914     this.buttons = [];
40915     this.allItems = [];
40916     this.addEvents({
40917         /**
40918          * @event clientvalidation
40919          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40920          * @param {Form} this
40921          * @param {Boolean} valid true if the form has passed client-side validation
40922          */
40923         clientvalidation: true,
40924         /**
40925          * @event rendered
40926          * Fires when the form is rendered
40927          * @param {Roo.form.Form} form
40928          */
40929         rendered : true
40930     });
40931     
40932     if (this.progressUrl) {
40933             // push a hidden field onto the list of fields..
40934             this.addxtype( {
40935                     xns: Roo.form, 
40936                     xtype : 'Hidden', 
40937                     name : 'UPLOAD_IDENTIFIER' 
40938             });
40939         }
40940         
40941     
40942     Roo.each(xitems, this.addxtype, this);
40943     
40944     
40945     
40946 };
40947
40948 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40949     /**
40950      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40951      */
40952     /**
40953      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40954      */
40955     /**
40956      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40957      */
40958     buttonAlign:'center',
40959
40960     /**
40961      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40962      */
40963     minButtonWidth:75,
40964
40965     /**
40966      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40967      * This property cascades to child containers if not set.
40968      */
40969     labelAlign:'left',
40970
40971     /**
40972      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40973      * fires a looping event with that state. This is required to bind buttons to the valid
40974      * state using the config value formBind:true on the button.
40975      */
40976     monitorValid : false,
40977
40978     /**
40979      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40980      */
40981     monitorPoll : 200,
40982     
40983     /**
40984      * @cfg {String} progressUrl - Url to return progress data 
40985      */
40986     
40987     progressUrl : false,
40988   
40989     /**
40990      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40991      * fields are added and the column is closed. If no fields are passed the column remains open
40992      * until end() is called.
40993      * @param {Object} config The config to pass to the column
40994      * @param {Field} field1 (optional)
40995      * @param {Field} field2 (optional)
40996      * @param {Field} etc (optional)
40997      * @return Column The column container object
40998      */
40999     column : function(c){
41000         var col = new Roo.form.Column(c);
41001         this.start(col);
41002         if(arguments.length > 1){ // duplicate code required because of Opera
41003             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41004             this.end();
41005         }
41006         return col;
41007     },
41008
41009     /**
41010      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41011      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41012      * until end() is called.
41013      * @param {Object} config The config to pass to the fieldset
41014      * @param {Field} field1 (optional)
41015      * @param {Field} field2 (optional)
41016      * @param {Field} etc (optional)
41017      * @return FieldSet The fieldset container object
41018      */
41019     fieldset : function(c){
41020         var fs = new Roo.form.FieldSet(c);
41021         this.start(fs);
41022         if(arguments.length > 1){ // duplicate code required because of Opera
41023             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41024             this.end();
41025         }
41026         return fs;
41027     },
41028
41029     /**
41030      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41031      * fields are added and the container is closed. If no fields are passed the container remains open
41032      * until end() is called.
41033      * @param {Object} config The config to pass to the Layout
41034      * @param {Field} field1 (optional)
41035      * @param {Field} field2 (optional)
41036      * @param {Field} etc (optional)
41037      * @return Layout The container object
41038      */
41039     container : function(c){
41040         var l = new Roo.form.Layout(c);
41041         this.start(l);
41042         if(arguments.length > 1){ // duplicate code required because of Opera
41043             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41044             this.end();
41045         }
41046         return l;
41047     },
41048
41049     /**
41050      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41051      * @param {Object} container A Roo.form.Layout or subclass of Layout
41052      * @return {Form} this
41053      */
41054     start : function(c){
41055         // cascade label info
41056         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41057         this.active.stack.push(c);
41058         c.ownerCt = this.active;
41059         this.active = c;
41060         return this;
41061     },
41062
41063     /**
41064      * Closes the current open container
41065      * @return {Form} this
41066      */
41067     end : function(){
41068         if(this.active == this.root){
41069             return this;
41070         }
41071         this.active = this.active.ownerCt;
41072         return this;
41073     },
41074
41075     /**
41076      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41077      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41078      * as the label of the field.
41079      * @param {Field} field1
41080      * @param {Field} field2 (optional)
41081      * @param {Field} etc. (optional)
41082      * @return {Form} this
41083      */
41084     add : function(){
41085         this.active.stack.push.apply(this.active.stack, arguments);
41086         this.allItems.push.apply(this.allItems,arguments);
41087         var r = [];
41088         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41089             if(a[i].isFormField){
41090                 r.push(a[i]);
41091             }
41092         }
41093         if(r.length > 0){
41094             Roo.form.Form.superclass.add.apply(this, r);
41095         }
41096         return this;
41097     },
41098     
41099
41100     
41101     
41102     
41103      /**
41104      * Find any element that has been added to a form, using it's ID or name
41105      * This can include framesets, columns etc. along with regular fields..
41106      * @param {String} id - id or name to find.
41107      
41108      * @return {Element} e - or false if nothing found.
41109      */
41110     findbyId : function(id)
41111     {
41112         var ret = false;
41113         if (!id) {
41114             return ret;
41115         }
41116         Ext.each(this.allItems, function(f){
41117             if (f.id == id || f.name == id ){
41118                 ret = f;
41119                 return false;
41120             }
41121         });
41122         return ret;
41123     },
41124
41125     
41126     
41127     /**
41128      * Render this form into the passed container. This should only be called once!
41129      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41130      * @return {Form} this
41131      */
41132     render : function(ct)
41133     {
41134         
41135         
41136         
41137         ct = Roo.get(ct);
41138         var o = this.autoCreate || {
41139             tag: 'form',
41140             method : this.method || 'POST',
41141             id : this.id || Roo.id()
41142         };
41143         this.initEl(ct.createChild(o));
41144
41145         this.root.render(this.el);
41146         
41147        
41148              
41149         this.items.each(function(f){
41150             f.render('x-form-el-'+f.id);
41151         });
41152
41153         if(this.buttons.length > 0){
41154             // tables are required to maintain order and for correct IE layout
41155             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41156                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41157                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41158             }}, null, true);
41159             var tr = tb.getElementsByTagName('tr')[0];
41160             for(var i = 0, len = this.buttons.length; i < len; i++) {
41161                 var b = this.buttons[i];
41162                 var td = document.createElement('td');
41163                 td.className = 'x-form-btn-td';
41164                 b.render(tr.appendChild(td));
41165             }
41166         }
41167         if(this.monitorValid){ // initialize after render
41168             this.startMonitoring();
41169         }
41170         this.fireEvent('rendered', this);
41171         return this;
41172     },
41173
41174     /**
41175      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41176      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41177      * object or a valid Roo.DomHelper element config
41178      * @param {Function} handler The function called when the button is clicked
41179      * @param {Object} scope (optional) The scope of the handler function
41180      * @return {Roo.Button}
41181      */
41182     addButton : function(config, handler, scope){
41183         var bc = {
41184             handler: handler,
41185             scope: scope,
41186             minWidth: this.minButtonWidth,
41187             hideParent:true
41188         };
41189         if(typeof config == "string"){
41190             bc.text = config;
41191         }else{
41192             Roo.apply(bc, config);
41193         }
41194         var btn = new Roo.Button(null, bc);
41195         this.buttons.push(btn);
41196         return btn;
41197     },
41198
41199      /**
41200      * Adds a series of form elements (using the xtype property as the factory method.
41201      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41202      * @param {Object} config 
41203      */
41204     
41205     addxtype : function()
41206     {
41207         var ar = Array.prototype.slice.call(arguments, 0);
41208         var ret = false;
41209         for(var i = 0; i < ar.length; i++) {
41210             if (!ar[i]) {
41211                 continue; // skip -- if this happends something invalid got sent, we 
41212                 // should ignore it, as basically that interface element will not show up
41213                 // and that should be pretty obvious!!
41214             }
41215             
41216             if (Roo.form[ar[i].xtype]) {
41217                 ar[i].form = this;
41218                 var fe = Roo.factory(ar[i], Roo.form);
41219                 if (!ret) {
41220                     ret = fe;
41221                 }
41222                 fe.form = this;
41223                 if (fe.store) {
41224                     fe.store.form = this;
41225                 }
41226                 if (fe.isLayout) {  
41227                          
41228                     this.start(fe);
41229                     this.allItems.push(fe);
41230                     if (fe.items && fe.addxtype) {
41231                         fe.addxtype.apply(fe, fe.items);
41232                         delete fe.items;
41233                     }
41234                      this.end();
41235                     continue;
41236                 }
41237                 
41238                 
41239                  
41240                 this.add(fe);
41241               //  console.log('adding ' + ar[i].xtype);
41242             }
41243             if (ar[i].xtype == 'Button') {  
41244                 //console.log('adding button');
41245                 //console.log(ar[i]);
41246                 this.addButton(ar[i]);
41247                 this.allItems.push(fe);
41248                 continue;
41249             }
41250             
41251             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41252                 alert('end is not supported on xtype any more, use items');
41253             //    this.end();
41254             //    //console.log('adding end');
41255             }
41256             
41257         }
41258         return ret;
41259     },
41260     
41261     /**
41262      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41263      * option "monitorValid"
41264      */
41265     startMonitoring : function(){
41266         if(!this.bound){
41267             this.bound = true;
41268             Roo.TaskMgr.start({
41269                 run : this.bindHandler,
41270                 interval : this.monitorPoll || 200,
41271                 scope: this
41272             });
41273         }
41274     },
41275
41276     /**
41277      * Stops monitoring of the valid state of this form
41278      */
41279     stopMonitoring : function(){
41280         this.bound = false;
41281     },
41282
41283     // private
41284     bindHandler : function(){
41285         if(!this.bound){
41286             return false; // stops binding
41287         }
41288         var valid = true;
41289         this.items.each(function(f){
41290             if(!f.isValid(true)){
41291                 valid = false;
41292                 return false;
41293             }
41294         });
41295         for(var i = 0, len = this.buttons.length; i < len; i++){
41296             var btn = this.buttons[i];
41297             if(btn.formBind === true && btn.disabled === valid){
41298                 btn.setDisabled(!valid);
41299             }
41300         }
41301         this.fireEvent('clientvalidation', this, valid);
41302     }
41303     
41304     
41305     
41306     
41307     
41308     
41309     
41310     
41311 });
41312
41313
41314 // back compat
41315 Roo.Form = Roo.form.Form;
41316 /*
41317  * Based on:
41318  * Ext JS Library 1.1.1
41319  * Copyright(c) 2006-2007, Ext JS, LLC.
41320  *
41321  * Originally Released Under LGPL - original licence link has changed is not relivant.
41322  *
41323  * Fork - LGPL
41324  * <script type="text/javascript">
41325  */
41326  
41327  /**
41328  * @class Roo.form.Action
41329  * Internal Class used to handle form actions
41330  * @constructor
41331  * @param {Roo.form.BasicForm} el The form element or its id
41332  * @param {Object} config Configuration options
41333  */
41334  
41335  
41336 // define the action interface
41337 Roo.form.Action = function(form, options){
41338     this.form = form;
41339     this.options = options || {};
41340 };
41341 /**
41342  * Client Validation Failed
41343  * @const 
41344  */
41345 Roo.form.Action.CLIENT_INVALID = 'client';
41346 /**
41347  * Server Validation Failed
41348  * @const 
41349  */
41350  Roo.form.Action.SERVER_INVALID = 'server';
41351  /**
41352  * Connect to Server Failed
41353  * @const 
41354  */
41355 Roo.form.Action.CONNECT_FAILURE = 'connect';
41356 /**
41357  * Reading Data from Server Failed
41358  * @const 
41359  */
41360 Roo.form.Action.LOAD_FAILURE = 'load';
41361
41362 Roo.form.Action.prototype = {
41363     type : 'default',
41364     failureType : undefined,
41365     response : undefined,
41366     result : undefined,
41367
41368     // interface method
41369     run : function(options){
41370
41371     },
41372
41373     // interface method
41374     success : function(response){
41375
41376     },
41377
41378     // interface method
41379     handleResponse : function(response){
41380
41381     },
41382
41383     // default connection failure
41384     failure : function(response){
41385         
41386         this.response = response;
41387         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41388         this.form.afterAction(this, false);
41389     },
41390
41391     processResponse : function(response){
41392         this.response = response;
41393         if(!response.responseText){
41394             return true;
41395         }
41396         this.result = this.handleResponse(response);
41397         return this.result;
41398     },
41399
41400     // utility functions used internally
41401     getUrl : function(appendParams){
41402         var url = this.options.url || this.form.url || this.form.el.dom.action;
41403         if(appendParams){
41404             var p = this.getParams();
41405             if(p){
41406                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41407             }
41408         }
41409         return url;
41410     },
41411
41412     getMethod : function(){
41413         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41414     },
41415
41416     getParams : function(){
41417         var bp = this.form.baseParams;
41418         var p = this.options.params;
41419         if(p){
41420             if(typeof p == "object"){
41421                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41422             }else if(typeof p == 'string' && bp){
41423                 p += '&' + Roo.urlEncode(bp);
41424             }
41425         }else if(bp){
41426             p = Roo.urlEncode(bp);
41427         }
41428         return p;
41429     },
41430
41431     createCallback : function(){
41432         return {
41433             success: this.success,
41434             failure: this.failure,
41435             scope: this,
41436             timeout: (this.form.timeout*1000),
41437             upload: this.form.fileUpload ? this.success : undefined
41438         };
41439     }
41440 };
41441
41442 Roo.form.Action.Submit = function(form, options){
41443     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41444 };
41445
41446 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41447     type : 'submit',
41448
41449     haveProgress : false,
41450     uploadComplete : false,
41451     
41452     // uploadProgress indicator.
41453     uploadProgress : function()
41454     {
41455         if (!this.form.progressUrl) {
41456             return;
41457         }
41458         
41459         if (!this.haveProgress) {
41460             Roo.MessageBox.progress("Uploading", "Uploading");
41461         }
41462         if (this.uploadComplete) {
41463            Roo.MessageBox.hide();
41464            return;
41465         }
41466         
41467         this.haveProgress = true;
41468    
41469         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41470         
41471         var c = new Roo.data.Connection();
41472         c.request({
41473             url : this.form.progressUrl,
41474             params: {
41475                 id : uid
41476             },
41477             method: 'GET',
41478             success : function(req){
41479                //console.log(data);
41480                 var rdata = false;
41481                 var edata;
41482                 try  {
41483                    rdata = Roo.decode(req.responseText)
41484                 } catch (e) {
41485                     Roo.log("Invalid data from server..");
41486                     Roo.log(edata);
41487                     return;
41488                 }
41489                 if (!rdata || !rdata.success) {
41490                     Roo.log(rdata);
41491                     return;
41492                 }
41493                 var data = rdata.data;
41494                 
41495                 if (this.uploadComplete) {
41496                    Roo.MessageBox.hide();
41497                    return;
41498                 }
41499                    
41500                 if (data){
41501                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41502                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41503                     );
41504                 }
41505                 this.uploadProgress.defer(2000,this);
41506             },
41507        
41508             failure: function(data) {
41509                 Roo.log('progress url failed ');
41510                 Roo.log(data);
41511             },
41512             scope : this
41513         });
41514            
41515     },
41516     
41517     
41518     run : function()
41519     {
41520         // run get Values on the form, so it syncs any secondary forms.
41521         this.form.getValues();
41522         
41523         var o = this.options;
41524         var method = this.getMethod();
41525         var isPost = method == 'POST';
41526         if(o.clientValidation === false || this.form.isValid()){
41527             
41528             if (this.form.progressUrl) {
41529                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41530                     (new Date() * 1) + '' + Math.random());
41531                     
41532             } 
41533             
41534             
41535             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41536                 form:this.form.el.dom,
41537                 url:this.getUrl(!isPost),
41538                 method: method,
41539                 params:isPost ? this.getParams() : null,
41540                 isUpload: this.form.fileUpload
41541             }));
41542             
41543             this.uploadProgress();
41544
41545         }else if (o.clientValidation !== false){ // client validation failed
41546             this.failureType = Roo.form.Action.CLIENT_INVALID;
41547             this.form.afterAction(this, false);
41548         }
41549     },
41550
41551     success : function(response)
41552     {
41553         this.uploadComplete= true;
41554         if (this.haveProgress) {
41555             Roo.MessageBox.hide();
41556         }
41557         
41558         
41559         var result = this.processResponse(response);
41560         if(result === true || result.success){
41561             this.form.afterAction(this, true);
41562             return;
41563         }
41564         if(result.errors){
41565             this.form.markInvalid(result.errors);
41566             this.failureType = Roo.form.Action.SERVER_INVALID;
41567         }
41568         this.form.afterAction(this, false);
41569     },
41570     failure : function(response)
41571     {
41572         this.uploadComplete= true;
41573         if (this.haveProgress) {
41574             Roo.MessageBox.hide();
41575         }
41576         
41577         
41578         this.response = response;
41579         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41580         this.form.afterAction(this, false);
41581     },
41582     
41583     handleResponse : function(response){
41584         if(this.form.errorReader){
41585             var rs = this.form.errorReader.read(response);
41586             var errors = [];
41587             if(rs.records){
41588                 for(var i = 0, len = rs.records.length; i < len; i++) {
41589                     var r = rs.records[i];
41590                     errors[i] = r.data;
41591                 }
41592             }
41593             if(errors.length < 1){
41594                 errors = null;
41595             }
41596             return {
41597                 success : rs.success,
41598                 errors : errors
41599             };
41600         }
41601         var ret = false;
41602         try {
41603             ret = Roo.decode(response.responseText);
41604         } catch (e) {
41605             ret = {
41606                 success: false,
41607                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41608                 errors : []
41609             };
41610         }
41611         return ret;
41612         
41613     }
41614 });
41615
41616
41617 Roo.form.Action.Load = function(form, options){
41618     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41619     this.reader = this.form.reader;
41620 };
41621
41622 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41623     type : 'load',
41624
41625     run : function(){
41626         
41627         Roo.Ajax.request(Roo.apply(
41628                 this.createCallback(), {
41629                     method:this.getMethod(),
41630                     url:this.getUrl(false),
41631                     params:this.getParams()
41632         }));
41633     },
41634
41635     success : function(response){
41636         
41637         var result = this.processResponse(response);
41638         if(result === true || !result.success || !result.data){
41639             this.failureType = Roo.form.Action.LOAD_FAILURE;
41640             this.form.afterAction(this, false);
41641             return;
41642         }
41643         this.form.clearInvalid();
41644         this.form.setValues(result.data);
41645         this.form.afterAction(this, true);
41646     },
41647
41648     handleResponse : function(response){
41649         if(this.form.reader){
41650             var rs = this.form.reader.read(response);
41651             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41652             return {
41653                 success : rs.success,
41654                 data : data
41655             };
41656         }
41657         return Roo.decode(response.responseText);
41658     }
41659 });
41660
41661 Roo.form.Action.ACTION_TYPES = {
41662     'load' : Roo.form.Action.Load,
41663     'submit' : Roo.form.Action.Submit
41664 };/*
41665  * Based on:
41666  * Ext JS Library 1.1.1
41667  * Copyright(c) 2006-2007, Ext JS, LLC.
41668  *
41669  * Originally Released Under LGPL - original licence link has changed is not relivant.
41670  *
41671  * Fork - LGPL
41672  * <script type="text/javascript">
41673  */
41674  
41675 /**
41676  * @class Roo.form.Layout
41677  * @extends Roo.Component
41678  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41679  * @constructor
41680  * @param {Object} config Configuration options
41681  */
41682 Roo.form.Layout = function(config){
41683     var xitems = [];
41684     if (config.items) {
41685         xitems = config.items;
41686         delete config.items;
41687     }
41688     Roo.form.Layout.superclass.constructor.call(this, config);
41689     this.stack = [];
41690     Roo.each(xitems, this.addxtype, this);
41691      
41692 };
41693
41694 Roo.extend(Roo.form.Layout, Roo.Component, {
41695     /**
41696      * @cfg {String/Object} autoCreate
41697      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41698      */
41699     /**
41700      * @cfg {String/Object/Function} style
41701      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41702      * a function which returns such a specification.
41703      */
41704     /**
41705      * @cfg {String} labelAlign
41706      * Valid values are "left," "top" and "right" (defaults to "left")
41707      */
41708     /**
41709      * @cfg {Number} labelWidth
41710      * Fixed width in pixels of all field labels (defaults to undefined)
41711      */
41712     /**
41713      * @cfg {Boolean} clear
41714      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41715      */
41716     clear : true,
41717     /**
41718      * @cfg {String} labelSeparator
41719      * The separator to use after field labels (defaults to ':')
41720      */
41721     labelSeparator : ':',
41722     /**
41723      * @cfg {Boolean} hideLabels
41724      * True to suppress the display of field labels in this layout (defaults to false)
41725      */
41726     hideLabels : false,
41727
41728     // private
41729     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41730     
41731     isLayout : true,
41732     
41733     // private
41734     onRender : function(ct, position){
41735         if(this.el){ // from markup
41736             this.el = Roo.get(this.el);
41737         }else {  // generate
41738             var cfg = this.getAutoCreate();
41739             this.el = ct.createChild(cfg, position);
41740         }
41741         if(this.style){
41742             this.el.applyStyles(this.style);
41743         }
41744         if(this.labelAlign){
41745             this.el.addClass('x-form-label-'+this.labelAlign);
41746         }
41747         if(this.hideLabels){
41748             this.labelStyle = "display:none";
41749             this.elementStyle = "padding-left:0;";
41750         }else{
41751             if(typeof this.labelWidth == 'number'){
41752                 this.labelStyle = "width:"+this.labelWidth+"px;";
41753                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41754             }
41755             if(this.labelAlign == 'top'){
41756                 this.labelStyle = "width:auto;";
41757                 this.elementStyle = "padding-left:0;";
41758             }
41759         }
41760         var stack = this.stack;
41761         var slen = stack.length;
41762         if(slen > 0){
41763             if(!this.fieldTpl){
41764                 var t = new Roo.Template(
41765                     '<div class="x-form-item {5}">',
41766                         '<label for="{0}" style="{2}">{1}{4}</label>',
41767                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41768                         '</div>',
41769                     '</div><div class="x-form-clear-left"></div>'
41770                 );
41771                 t.disableFormats = true;
41772                 t.compile();
41773                 Roo.form.Layout.prototype.fieldTpl = t;
41774             }
41775             for(var i = 0; i < slen; i++) {
41776                 if(stack[i].isFormField){
41777                     this.renderField(stack[i]);
41778                 }else{
41779                     this.renderComponent(stack[i]);
41780                 }
41781             }
41782         }
41783         if(this.clear){
41784             this.el.createChild({cls:'x-form-clear'});
41785         }
41786     },
41787
41788     // private
41789     renderField : function(f){
41790         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41791                f.id, //0
41792                f.fieldLabel, //1
41793                f.labelStyle||this.labelStyle||'', //2
41794                this.elementStyle||'', //3
41795                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41796                f.itemCls||this.itemCls||''  //5
41797        ], true).getPrevSibling());
41798     },
41799
41800     // private
41801     renderComponent : function(c){
41802         c.render(c.isLayout ? this.el : this.el.createChild());    
41803     },
41804     /**
41805      * Adds a object form elements (using the xtype property as the factory method.)
41806      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41807      * @param {Object} config 
41808      */
41809     addxtype : function(o)
41810     {
41811         // create the lement.
41812         o.form = this.form;
41813         var fe = Roo.factory(o, Roo.form);
41814         this.form.allItems.push(fe);
41815         this.stack.push(fe);
41816         
41817         if (fe.isFormField) {
41818             this.form.items.add(fe);
41819         }
41820          
41821         return fe;
41822     }
41823 });
41824
41825 /**
41826  * @class Roo.form.Column
41827  * @extends Roo.form.Layout
41828  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41829  * @constructor
41830  * @param {Object} config Configuration options
41831  */
41832 Roo.form.Column = function(config){
41833     Roo.form.Column.superclass.constructor.call(this, config);
41834 };
41835
41836 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41837     /**
41838      * @cfg {Number/String} width
41839      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41840      */
41841     /**
41842      * @cfg {String/Object} autoCreate
41843      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41844      */
41845
41846     // private
41847     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41848
41849     // private
41850     onRender : function(ct, position){
41851         Roo.form.Column.superclass.onRender.call(this, ct, position);
41852         if(this.width){
41853             this.el.setWidth(this.width);
41854         }
41855     }
41856 });
41857
41858
41859 /**
41860  * @class Roo.form.Row
41861  * @extends Roo.form.Layout
41862  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41863  * @constructor
41864  * @param {Object} config Configuration options
41865  */
41866
41867  
41868 Roo.form.Row = function(config){
41869     Roo.form.Row.superclass.constructor.call(this, config);
41870 };
41871  
41872 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41873       /**
41874      * @cfg {Number/String} width
41875      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41876      */
41877     /**
41878      * @cfg {Number/String} height
41879      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41880      */
41881     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41882     
41883     padWidth : 20,
41884     // private
41885     onRender : function(ct, position){
41886         //console.log('row render');
41887         if(!this.rowTpl){
41888             var t = new Roo.Template(
41889                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41890                     '<label for="{0}" style="{2}">{1}{4}</label>',
41891                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41892                     '</div>',
41893                 '</div>'
41894             );
41895             t.disableFormats = true;
41896             t.compile();
41897             Roo.form.Layout.prototype.rowTpl = t;
41898         }
41899         this.fieldTpl = this.rowTpl;
41900         
41901         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41902         var labelWidth = 100;
41903         
41904         if ((this.labelAlign != 'top')) {
41905             if (typeof this.labelWidth == 'number') {
41906                 labelWidth = this.labelWidth
41907             }
41908             this.padWidth =  20 + labelWidth;
41909             
41910         }
41911         
41912         Roo.form.Column.superclass.onRender.call(this, ct, position);
41913         if(this.width){
41914             this.el.setWidth(this.width);
41915         }
41916         if(this.height){
41917             this.el.setHeight(this.height);
41918         }
41919     },
41920     
41921     // private
41922     renderField : function(f){
41923         f.fieldEl = this.fieldTpl.append(this.el, [
41924                f.id, f.fieldLabel,
41925                f.labelStyle||this.labelStyle||'',
41926                this.elementStyle||'',
41927                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41928                f.itemCls||this.itemCls||'',
41929                f.width ? f.width + this.padWidth : 160 + this.padWidth
41930        ],true);
41931     }
41932 });
41933  
41934
41935 /**
41936  * @class Roo.form.FieldSet
41937  * @extends Roo.form.Layout
41938  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41939  * @constructor
41940  * @param {Object} config Configuration options
41941  */
41942 Roo.form.FieldSet = function(config){
41943     Roo.form.FieldSet.superclass.constructor.call(this, config);
41944 };
41945
41946 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41947     /**
41948      * @cfg {String} legend
41949      * The text to display as the legend for the FieldSet (defaults to '')
41950      */
41951     /**
41952      * @cfg {String/Object} autoCreate
41953      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41954      */
41955
41956     // private
41957     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41958
41959     // private
41960     onRender : function(ct, position){
41961         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41962         if(this.legend){
41963             this.setLegend(this.legend);
41964         }
41965     },
41966
41967     // private
41968     setLegend : function(text){
41969         if(this.rendered){
41970             this.el.child('legend').update(text);
41971         }
41972     }
41973 });/*
41974  * Based on:
41975  * Ext JS Library 1.1.1
41976  * Copyright(c) 2006-2007, Ext JS, LLC.
41977  *
41978  * Originally Released Under LGPL - original licence link has changed is not relivant.
41979  *
41980  * Fork - LGPL
41981  * <script type="text/javascript">
41982  */
41983 /**
41984  * @class Roo.form.VTypes
41985  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41986  * @singleton
41987  */
41988 Roo.form.VTypes = function(){
41989     // closure these in so they are only created once.
41990     var alpha = /^[a-zA-Z_]+$/;
41991     var alphanum = /^[a-zA-Z0-9_]+$/;
41992     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41993     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41994
41995     // All these messages and functions are configurable
41996     return {
41997         /**
41998          * The function used to validate email addresses
41999          * @param {String} value The email address
42000          */
42001         'email' : function(v){
42002             return email.test(v);
42003         },
42004         /**
42005          * The error text to display when the email validation function returns false
42006          * @type String
42007          */
42008         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42009         /**
42010          * The keystroke filter mask to be applied on email input
42011          * @type RegExp
42012          */
42013         'emailMask' : /[a-z0-9_\.\-@]/i,
42014
42015         /**
42016          * The function used to validate URLs
42017          * @param {String} value The URL
42018          */
42019         'url' : function(v){
42020             return url.test(v);
42021         },
42022         /**
42023          * The error text to display when the url validation function returns false
42024          * @type String
42025          */
42026         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42027         
42028         /**
42029          * The function used to validate alpha values
42030          * @param {String} value The value
42031          */
42032         'alpha' : function(v){
42033             return alpha.test(v);
42034         },
42035         /**
42036          * The error text to display when the alpha validation function returns false
42037          * @type String
42038          */
42039         'alphaText' : 'This field should only contain letters and _',
42040         /**
42041          * The keystroke filter mask to be applied on alpha input
42042          * @type RegExp
42043          */
42044         'alphaMask' : /[a-z_]/i,
42045
42046         /**
42047          * The function used to validate alphanumeric values
42048          * @param {String} value The value
42049          */
42050         'alphanum' : function(v){
42051             return alphanum.test(v);
42052         },
42053         /**
42054          * The error text to display when the alphanumeric validation function returns false
42055          * @type String
42056          */
42057         'alphanumText' : 'This field should only contain letters, numbers and _',
42058         /**
42059          * The keystroke filter mask to be applied on alphanumeric input
42060          * @type RegExp
42061          */
42062         'alphanumMask' : /[a-z0-9_]/i
42063     };
42064 }();//<script type="text/javascript">
42065
42066 /**
42067  * @class Roo.form.FCKeditor
42068  * @extends Roo.form.TextArea
42069  * Wrapper around the FCKEditor http://www.fckeditor.net
42070  * @constructor
42071  * Creates a new FCKeditor
42072  * @param {Object} config Configuration options
42073  */
42074 Roo.form.FCKeditor = function(config){
42075     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42076     this.addEvents({
42077          /**
42078          * @event editorinit
42079          * Fired when the editor is initialized - you can add extra handlers here..
42080          * @param {FCKeditor} this
42081          * @param {Object} the FCK object.
42082          */
42083         editorinit : true
42084     });
42085     
42086     
42087 };
42088 Roo.form.FCKeditor.editors = { };
42089 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42090 {
42091     //defaultAutoCreate : {
42092     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42093     //},
42094     // private
42095     /**
42096      * @cfg {Object} fck options - see fck manual for details.
42097      */
42098     fckconfig : false,
42099     
42100     /**
42101      * @cfg {Object} fck toolbar set (Basic or Default)
42102      */
42103     toolbarSet : 'Basic',
42104     /**
42105      * @cfg {Object} fck BasePath
42106      */ 
42107     basePath : '/fckeditor/',
42108     
42109     
42110     frame : false,
42111     
42112     value : '',
42113     
42114    
42115     onRender : function(ct, position)
42116     {
42117         if(!this.el){
42118             this.defaultAutoCreate = {
42119                 tag: "textarea",
42120                 style:"width:300px;height:60px;",
42121                 autocomplete: "off"
42122             };
42123         }
42124         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42125         /*
42126         if(this.grow){
42127             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42128             if(this.preventScrollbars){
42129                 this.el.setStyle("overflow", "hidden");
42130             }
42131             this.el.setHeight(this.growMin);
42132         }
42133         */
42134         //console.log('onrender' + this.getId() );
42135         Roo.form.FCKeditor.editors[this.getId()] = this;
42136          
42137
42138         this.replaceTextarea() ;
42139         
42140     },
42141     
42142     getEditor : function() {
42143         return this.fckEditor;
42144     },
42145     /**
42146      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42147      * @param {Mixed} value The value to set
42148      */
42149     
42150     
42151     setValue : function(value)
42152     {
42153         //console.log('setValue: ' + value);
42154         
42155         if(typeof(value) == 'undefined') { // not sure why this is happending...
42156             return;
42157         }
42158         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42159         
42160         //if(!this.el || !this.getEditor()) {
42161         //    this.value = value;
42162             //this.setValue.defer(100,this,[value]);    
42163         //    return;
42164         //} 
42165         
42166         if(!this.getEditor()) {
42167             return;
42168         }
42169         
42170         this.getEditor().SetData(value);
42171         
42172         //
42173
42174     },
42175
42176     /**
42177      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42178      * @return {Mixed} value The field value
42179      */
42180     getValue : function()
42181     {
42182         
42183         if (this.frame && this.frame.dom.style.display == 'none') {
42184             return Roo.form.FCKeditor.superclass.getValue.call(this);
42185         }
42186         
42187         if(!this.el || !this.getEditor()) {
42188            
42189            // this.getValue.defer(100,this); 
42190             return this.value;
42191         }
42192        
42193         
42194         var value=this.getEditor().GetData();
42195         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42196         return Roo.form.FCKeditor.superclass.getValue.call(this);
42197         
42198
42199     },
42200
42201     /**
42202      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42203      * @return {Mixed} value The field value
42204      */
42205     getRawValue : function()
42206     {
42207         if (this.frame && this.frame.dom.style.display == 'none') {
42208             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42209         }
42210         
42211         if(!this.el || !this.getEditor()) {
42212             //this.getRawValue.defer(100,this); 
42213             return this.value;
42214             return;
42215         }
42216         
42217         
42218         
42219         var value=this.getEditor().GetData();
42220         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42221         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42222          
42223     },
42224     
42225     setSize : function(w,h) {
42226         
42227         
42228         
42229         //if (this.frame && this.frame.dom.style.display == 'none') {
42230         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42231         //    return;
42232         //}
42233         //if(!this.el || !this.getEditor()) {
42234         //    this.setSize.defer(100,this, [w,h]); 
42235         //    return;
42236         //}
42237         
42238         
42239         
42240         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42241         
42242         this.frame.dom.setAttribute('width', w);
42243         this.frame.dom.setAttribute('height', h);
42244         this.frame.setSize(w,h);
42245         
42246     },
42247     
42248     toggleSourceEdit : function(value) {
42249         
42250       
42251          
42252         this.el.dom.style.display = value ? '' : 'none';
42253         this.frame.dom.style.display = value ?  'none' : '';
42254         
42255     },
42256     
42257     
42258     focus: function(tag)
42259     {
42260         if (this.frame.dom.style.display == 'none') {
42261             return Roo.form.FCKeditor.superclass.focus.call(this);
42262         }
42263         if(!this.el || !this.getEditor()) {
42264             this.focus.defer(100,this, [tag]); 
42265             return;
42266         }
42267         
42268         
42269         
42270         
42271         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42272         this.getEditor().Focus();
42273         if (tgs.length) {
42274             if (!this.getEditor().Selection.GetSelection()) {
42275                 this.focus.defer(100,this, [tag]); 
42276                 return;
42277             }
42278             
42279             
42280             var r = this.getEditor().EditorDocument.createRange();
42281             r.setStart(tgs[0],0);
42282             r.setEnd(tgs[0],0);
42283             this.getEditor().Selection.GetSelection().removeAllRanges();
42284             this.getEditor().Selection.GetSelection().addRange(r);
42285             this.getEditor().Focus();
42286         }
42287         
42288     },
42289     
42290     
42291     
42292     replaceTextarea : function()
42293     {
42294         if ( document.getElementById( this.getId() + '___Frame' ) )
42295             return ;
42296         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42297         //{
42298             // We must check the elements firstly using the Id and then the name.
42299         var oTextarea = document.getElementById( this.getId() );
42300         
42301         var colElementsByName = document.getElementsByName( this.getId() ) ;
42302          
42303         oTextarea.style.display = 'none' ;
42304
42305         if ( oTextarea.tabIndex ) {            
42306             this.TabIndex = oTextarea.tabIndex ;
42307         }
42308         
42309         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42310         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42311         this.frame = Roo.get(this.getId() + '___Frame')
42312     },
42313     
42314     _getConfigHtml : function()
42315     {
42316         var sConfig = '' ;
42317
42318         for ( var o in this.fckconfig ) {
42319             sConfig += sConfig.length > 0  ? '&amp;' : '';
42320             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42321         }
42322
42323         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42324     },
42325     
42326     
42327     _getIFrameHtml : function()
42328     {
42329         var sFile = 'fckeditor.html' ;
42330         /* no idea what this is about..
42331         try
42332         {
42333             if ( (/fcksource=true/i).test( window.top.location.search ) )
42334                 sFile = 'fckeditor.original.html' ;
42335         }
42336         catch (e) { 
42337         */
42338
42339         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42340         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42341         
42342         
42343         var html = '<iframe id="' + this.getId() +
42344             '___Frame" src="' + sLink +
42345             '" width="' + this.width +
42346             '" height="' + this.height + '"' +
42347             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42348             ' frameborder="0" scrolling="no"></iframe>' ;
42349
42350         return html ;
42351     },
42352     
42353     _insertHtmlBefore : function( html, element )
42354     {
42355         if ( element.insertAdjacentHTML )       {
42356             // IE
42357             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42358         } else { // Gecko
42359             var oRange = document.createRange() ;
42360             oRange.setStartBefore( element ) ;
42361             var oFragment = oRange.createContextualFragment( html );
42362             element.parentNode.insertBefore( oFragment, element ) ;
42363         }
42364     }
42365     
42366     
42367   
42368     
42369     
42370     
42371     
42372
42373 });
42374
42375 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42376
42377 function FCKeditor_OnComplete(editorInstance){
42378     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42379     f.fckEditor = editorInstance;
42380     //console.log("loaded");
42381     f.fireEvent('editorinit', f, editorInstance);
42382
42383   
42384
42385  
42386
42387
42388
42389
42390
42391
42392
42393
42394
42395
42396
42397
42398
42399
42400
42401 //<script type="text/javascript">
42402 /**
42403  * @class Roo.form.GridField
42404  * @extends Roo.form.Field
42405  * Embed a grid (or editable grid into a form)
42406  * STATUS ALPHA
42407  * 
42408  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42409  * it needs 
42410  * xgrid.store = Roo.data.Store
42411  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42412  * xgrid.store.reader = Roo.data.JsonReader 
42413  * 
42414  * 
42415  * @constructor
42416  * Creates a new GridField
42417  * @param {Object} config Configuration options
42418  */
42419 Roo.form.GridField = function(config){
42420     Roo.form.GridField.superclass.constructor.call(this, config);
42421      
42422 };
42423
42424 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42425     /**
42426      * @cfg {Number} width  - used to restrict width of grid..
42427      */
42428     width : 100,
42429     /**
42430      * @cfg {Number} height - used to restrict height of grid..
42431      */
42432     height : 50,
42433      /**
42434      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42435          * 
42436          *}
42437      */
42438     xgrid : false, 
42439     /**
42440      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42441      * {tag: "input", type: "checkbox", autocomplete: "off"})
42442      */
42443    // defaultAutoCreate : { tag: 'div' },
42444     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42445     /**
42446      * @cfg {String} addTitle Text to include for adding a title.
42447      */
42448     addTitle : false,
42449     //
42450     onResize : function(){
42451         Roo.form.Field.superclass.onResize.apply(this, arguments);
42452     },
42453
42454     initEvents : function(){
42455         // Roo.form.Checkbox.superclass.initEvents.call(this);
42456         // has no events...
42457        
42458     },
42459
42460
42461     getResizeEl : function(){
42462         return this.wrap;
42463     },
42464
42465     getPositionEl : function(){
42466         return this.wrap;
42467     },
42468
42469     // private
42470     onRender : function(ct, position){
42471         
42472         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42473         var style = this.style;
42474         delete this.style;
42475         
42476         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42477         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42478         this.viewEl = this.wrap.createChild({ tag: 'div' });
42479         if (style) {
42480             this.viewEl.applyStyles(style);
42481         }
42482         if (this.width) {
42483             this.viewEl.setWidth(this.width);
42484         }
42485         if (this.height) {
42486             this.viewEl.setHeight(this.height);
42487         }
42488         //if(this.inputValue !== undefined){
42489         //this.setValue(this.value);
42490         
42491         
42492         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42493         
42494         
42495         this.grid.render();
42496         this.grid.getDataSource().on('remove', this.refreshValue, this);
42497         this.grid.getDataSource().on('update', this.refreshValue, this);
42498         this.grid.on('afteredit', this.refreshValue, this);
42499  
42500     },
42501      
42502     
42503     /**
42504      * Sets the value of the item. 
42505      * @param {String} either an object  or a string..
42506      */
42507     setValue : function(v){
42508         //this.value = v;
42509         v = v || []; // empty set..
42510         // this does not seem smart - it really only affects memoryproxy grids..
42511         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42512             var ds = this.grid.getDataSource();
42513             // assumes a json reader..
42514             var data = {}
42515             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42516             ds.loadData( data);
42517         }
42518         Roo.form.GridField.superclass.setValue.call(this, v);
42519         this.refreshValue();
42520         // should load data in the grid really....
42521     },
42522     
42523     // private
42524     refreshValue: function() {
42525          var val = [];
42526         this.grid.getDataSource().each(function(r) {
42527             val.push(r.data);
42528         });
42529         this.el.dom.value = Roo.encode(val);
42530     }
42531     
42532      
42533     
42534     
42535 });/*
42536  * Based on:
42537  * Ext JS Library 1.1.1
42538  * Copyright(c) 2006-2007, Ext JS, LLC.
42539  *
42540  * Originally Released Under LGPL - original licence link has changed is not relivant.
42541  *
42542  * Fork - LGPL
42543  * <script type="text/javascript">
42544  */
42545 /**
42546  * @class Roo.form.DisplayField
42547  * @extends Roo.form.Field
42548  * A generic Field to display non-editable data.
42549  * @constructor
42550  * Creates a new Display Field item.
42551  * @param {Object} config Configuration options
42552  */
42553 Roo.form.DisplayField = function(config){
42554     Roo.form.DisplayField.superclass.constructor.call(this, config);
42555     
42556 };
42557
42558 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42559     inputType:      'hidden',
42560     allowBlank:     true,
42561     readOnly:         true,
42562     
42563  
42564     /**
42565      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42566      */
42567     focusClass : undefined,
42568     /**
42569      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42570      */
42571     fieldClass: 'x-form-field',
42572     
42573      /**
42574      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42575      */
42576     valueRenderer: undefined,
42577     
42578     width: 100,
42579     /**
42580      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42581      * {tag: "input", type: "checkbox", autocomplete: "off"})
42582      */
42583      
42584  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42585
42586     onResize : function(){
42587         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42588         
42589     },
42590
42591     initEvents : function(){
42592         // Roo.form.Checkbox.superclass.initEvents.call(this);
42593         // has no events...
42594        
42595     },
42596
42597
42598     getResizeEl : function(){
42599         return this.wrap;
42600     },
42601
42602     getPositionEl : function(){
42603         return this.wrap;
42604     },
42605
42606     // private
42607     onRender : function(ct, position){
42608         
42609         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42610         //if(this.inputValue !== undefined){
42611         this.wrap = this.el.wrap();
42612         
42613         this.viewEl = this.wrap.createChild({ tag: 'div'});
42614         
42615         if (this.bodyStyle) {
42616             this.viewEl.applyStyles(this.bodyStyle);
42617         }
42618         //this.viewEl.setStyle('padding', '2px');
42619         
42620         this.setValue(this.value);
42621         
42622     },
42623 /*
42624     // private
42625     initValue : Roo.emptyFn,
42626
42627   */
42628
42629         // private
42630     onClick : function(){
42631         
42632     },
42633
42634     /**
42635      * Sets the checked state of the checkbox.
42636      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42637      */
42638     setValue : function(v){
42639         this.value = v;
42640         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42641         // this might be called before we have a dom element..
42642         if (!this.viewEl) {
42643             return;
42644         }
42645         this.viewEl.dom.innerHTML = html;
42646         Roo.form.DisplayField.superclass.setValue.call(this, v);
42647
42648     }
42649 });//<script type="text/javasscript">
42650  
42651
42652 /**
42653  * @class Roo.DDView
42654  * A DnD enabled version of Roo.View.
42655  * @param {Element/String} container The Element in which to create the View.
42656  * @param {String} tpl The template string used to create the markup for each element of the View
42657  * @param {Object} config The configuration properties. These include all the config options of
42658  * {@link Roo.View} plus some specific to this class.<br>
42659  * <p>
42660  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42661  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42662  * <p>
42663  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42664 .x-view-drag-insert-above {
42665         border-top:1px dotted #3366cc;
42666 }
42667 .x-view-drag-insert-below {
42668         border-bottom:1px dotted #3366cc;
42669 }
42670 </code></pre>
42671  * 
42672  */
42673  
42674 Roo.DDView = function(container, tpl, config) {
42675     Roo.DDView.superclass.constructor.apply(this, arguments);
42676     this.getEl().setStyle("outline", "0px none");
42677     this.getEl().unselectable();
42678     if (this.dragGroup) {
42679                 this.setDraggable(this.dragGroup.split(","));
42680     }
42681     if (this.dropGroup) {
42682                 this.setDroppable(this.dropGroup.split(","));
42683     }
42684     if (this.deletable) {
42685         this.setDeletable();
42686     }
42687     this.isDirtyFlag = false;
42688         this.addEvents({
42689                 "drop" : true
42690         });
42691 };
42692
42693 Roo.extend(Roo.DDView, Roo.View, {
42694 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42695 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42696 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42697 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42698
42699         isFormField: true,
42700
42701         reset: Roo.emptyFn,
42702         
42703         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42704
42705         validate: function() {
42706                 return true;
42707         },
42708         
42709         destroy: function() {
42710                 this.purgeListeners();
42711                 this.getEl.removeAllListeners();
42712                 this.getEl().remove();
42713                 if (this.dragZone) {
42714                         if (this.dragZone.destroy) {
42715                                 this.dragZone.destroy();
42716                         }
42717                 }
42718                 if (this.dropZone) {
42719                         if (this.dropZone.destroy) {
42720                                 this.dropZone.destroy();
42721                         }
42722                 }
42723         },
42724
42725 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42726         getName: function() {
42727                 return this.name;
42728         },
42729
42730 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42731         setValue: function(v) {
42732                 if (!this.store) {
42733                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42734                 }
42735                 var data = {};
42736                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42737                 this.store.proxy = new Roo.data.MemoryProxy(data);
42738                 this.store.load();
42739         },
42740
42741 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42742         getValue: function() {
42743                 var result = '(';
42744                 this.store.each(function(rec) {
42745                         result += rec.id + ',';
42746                 });
42747                 return result.substr(0, result.length - 1) + ')';
42748         },
42749         
42750         getIds: function() {
42751                 var i = 0, result = new Array(this.store.getCount());
42752                 this.store.each(function(rec) {
42753                         result[i++] = rec.id;
42754                 });
42755                 return result;
42756         },
42757         
42758         isDirty: function() {
42759                 return this.isDirtyFlag;
42760         },
42761
42762 /**
42763  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42764  *      whole Element becomes the target, and this causes the drop gesture to append.
42765  */
42766     getTargetFromEvent : function(e) {
42767                 var target = e.getTarget();
42768                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42769                 target = target.parentNode;
42770                 }
42771                 if (!target) {
42772                         target = this.el.dom.lastChild || this.el.dom;
42773                 }
42774                 return target;
42775     },
42776
42777 /**
42778  *      Create the drag data which consists of an object which has the property "ddel" as
42779  *      the drag proxy element. 
42780  */
42781     getDragData : function(e) {
42782         var target = this.findItemFromChild(e.getTarget());
42783                 if(target) {
42784                         this.handleSelection(e);
42785                         var selNodes = this.getSelectedNodes();
42786             var dragData = {
42787                 source: this,
42788                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42789                 nodes: selNodes,
42790                 records: []
42791                         };
42792                         var selectedIndices = this.getSelectedIndexes();
42793                         for (var i = 0; i < selectedIndices.length; i++) {
42794                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42795                         }
42796                         if (selNodes.length == 1) {
42797                                 dragData.ddel = target.cloneNode(true); // the div element
42798                         } else {
42799                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42800                                 div.className = 'multi-proxy';
42801                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42802                                         div.appendChild(selNodes[i].cloneNode(true));
42803                                 }
42804                                 dragData.ddel = div;
42805                         }
42806             //console.log(dragData)
42807             //console.log(dragData.ddel.innerHTML)
42808                         return dragData;
42809                 }
42810         //console.log('nodragData')
42811                 return false;
42812     },
42813     
42814 /**     Specify to which ddGroup items in this DDView may be dragged. */
42815     setDraggable: function(ddGroup) {
42816         if (ddGroup instanceof Array) {
42817                 Roo.each(ddGroup, this.setDraggable, this);
42818                 return;
42819         }
42820         if (this.dragZone) {
42821                 this.dragZone.addToGroup(ddGroup);
42822         } else {
42823                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42824                                 containerScroll: true,
42825                                 ddGroup: ddGroup 
42826
42827                         });
42828 //                      Draggability implies selection. DragZone's mousedown selects the element.
42829                         if (!this.multiSelect) { this.singleSelect = true; }
42830
42831 //                      Wire the DragZone's handlers up to methods in *this*
42832                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42833                 }
42834     },
42835
42836 /**     Specify from which ddGroup this DDView accepts drops. */
42837     setDroppable: function(ddGroup) {
42838         if (ddGroup instanceof Array) {
42839                 Roo.each(ddGroup, this.setDroppable, this);
42840                 return;
42841         }
42842         if (this.dropZone) {
42843                 this.dropZone.addToGroup(ddGroup);
42844         } else {
42845                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42846                                 containerScroll: true,
42847                                 ddGroup: ddGroup
42848                         });
42849
42850 //                      Wire the DropZone's handlers up to methods in *this*
42851                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42852                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42853                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42854                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42855                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42856                 }
42857     },
42858
42859 /**     Decide whether to drop above or below a View node. */
42860     getDropPoint : function(e, n, dd){
42861         if (n == this.el.dom) { return "above"; }
42862                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42863                 var c = t + (b - t) / 2;
42864                 var y = Roo.lib.Event.getPageY(e);
42865                 if(y <= c) {
42866                         return "above";
42867                 }else{
42868                         return "below";
42869                 }
42870     },
42871
42872     onNodeEnter : function(n, dd, e, data){
42873                 return false;
42874     },
42875     
42876     onNodeOver : function(n, dd, e, data){
42877                 var pt = this.getDropPoint(e, n, dd);
42878                 // set the insert point style on the target node
42879                 var dragElClass = this.dropNotAllowed;
42880                 if (pt) {
42881                         var targetElClass;
42882                         if (pt == "above"){
42883                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42884                                 targetElClass = "x-view-drag-insert-above";
42885                         } else {
42886                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42887                                 targetElClass = "x-view-drag-insert-below";
42888                         }
42889                         if (this.lastInsertClass != targetElClass){
42890                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42891                                 this.lastInsertClass = targetElClass;
42892                         }
42893                 }
42894                 return dragElClass;
42895         },
42896
42897     onNodeOut : function(n, dd, e, data){
42898                 this.removeDropIndicators(n);
42899     },
42900
42901     onNodeDrop : function(n, dd, e, data){
42902         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42903                 return false;
42904         }
42905         var pt = this.getDropPoint(e, n, dd);
42906                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42907                 if (pt == "below") { insertAt++; }
42908                 for (var i = 0; i < data.records.length; i++) {
42909                         var r = data.records[i];
42910                         var dup = this.store.getById(r.id);
42911                         if (dup && (dd != this.dragZone)) {
42912                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42913                         } else {
42914                                 if (data.copy) {
42915                                         this.store.insert(insertAt++, r.copy());
42916                                 } else {
42917                                         data.source.isDirtyFlag = true;
42918                                         r.store.remove(r);
42919                                         this.store.insert(insertAt++, r);
42920                                 }
42921                                 this.isDirtyFlag = true;
42922                         }
42923                 }
42924                 this.dragZone.cachedTarget = null;
42925                 return true;
42926     },
42927
42928     removeDropIndicators : function(n){
42929                 if(n){
42930                         Roo.fly(n).removeClass([
42931                                 "x-view-drag-insert-above",
42932                                 "x-view-drag-insert-below"]);
42933                         this.lastInsertClass = "_noclass";
42934                 }
42935     },
42936
42937 /**
42938  *      Utility method. Add a delete option to the DDView's context menu.
42939  *      @param {String} imageUrl The URL of the "delete" icon image.
42940  */
42941         setDeletable: function(imageUrl) {
42942                 if (!this.singleSelect && !this.multiSelect) {
42943                         this.singleSelect = true;
42944                 }
42945                 var c = this.getContextMenu();
42946                 this.contextMenu.on("itemclick", function(item) {
42947                         switch (item.id) {
42948                                 case "delete":
42949                                         this.remove(this.getSelectedIndexes());
42950                                         break;
42951                         }
42952                 }, this);
42953                 this.contextMenu.add({
42954                         icon: imageUrl,
42955                         id: "delete",
42956                         text: 'Delete'
42957                 });
42958         },
42959         
42960 /**     Return the context menu for this DDView. */
42961         getContextMenu: function() {
42962                 if (!this.contextMenu) {
42963 //                      Create the View's context menu
42964                         this.contextMenu = new Roo.menu.Menu({
42965                                 id: this.id + "-contextmenu"
42966                         });
42967                         this.el.on("contextmenu", this.showContextMenu, this);
42968                 }
42969                 return this.contextMenu;
42970         },
42971         
42972         disableContextMenu: function() {
42973                 if (this.contextMenu) {
42974                         this.el.un("contextmenu", this.showContextMenu, this);
42975                 }
42976         },
42977
42978         showContextMenu: function(e, item) {
42979         item = this.findItemFromChild(e.getTarget());
42980                 if (item) {
42981                         e.stopEvent();
42982                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42983                         this.contextMenu.showAt(e.getXY());
42984             }
42985     },
42986
42987 /**
42988  *      Remove {@link Roo.data.Record}s at the specified indices.
42989  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42990  */
42991     remove: function(selectedIndices) {
42992                 selectedIndices = [].concat(selectedIndices);
42993                 for (var i = 0; i < selectedIndices.length; i++) {
42994                         var rec = this.store.getAt(selectedIndices[i]);
42995                         this.store.remove(rec);
42996                 }
42997     },
42998
42999 /**
43000  *      Double click fires the event, but also, if this is draggable, and there is only one other
43001  *      related DropZone, it transfers the selected node.
43002  */
43003     onDblClick : function(e){
43004         var item = this.findItemFromChild(e.getTarget());
43005         if(item){
43006             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43007                 return false;
43008             }
43009             if (this.dragGroup) {
43010                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43011                     while (targets.indexOf(this.dropZone) > -1) {
43012                             targets.remove(this.dropZone);
43013                                 }
43014                     if (targets.length == 1) {
43015                                         this.dragZone.cachedTarget = null;
43016                         var el = Roo.get(targets[0].getEl());
43017                         var box = el.getBox(true);
43018                         targets[0].onNodeDrop(el.dom, {
43019                                 target: el.dom,
43020                                 xy: [box.x, box.y + box.height - 1]
43021                         }, null, this.getDragData(e));
43022                     }
43023                 }
43024         }
43025     },
43026     
43027     handleSelection: function(e) {
43028                 this.dragZone.cachedTarget = null;
43029         var item = this.findItemFromChild(e.getTarget());
43030         if (!item) {
43031                 this.clearSelections(true);
43032                 return;
43033         }
43034                 if (item && (this.multiSelect || this.singleSelect)){
43035                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43036                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43037                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43038                                 this.unselect(item);
43039                         } else {
43040                                 this.select(item, this.multiSelect && e.ctrlKey);
43041                                 this.lastSelection = item;
43042                         }
43043                 }
43044     },
43045
43046     onItemClick : function(item, index, e){
43047                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43048                         return false;
43049                 }
43050                 return true;
43051     },
43052
43053     unselect : function(nodeInfo, suppressEvent){
43054                 var node = this.getNode(nodeInfo);
43055                 if(node && this.isSelected(node)){
43056                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43057                                 Roo.fly(node).removeClass(this.selectedClass);
43058                                 this.selections.remove(node);
43059                                 if(!suppressEvent){
43060                                         this.fireEvent("selectionchange", this, this.selections);
43061                                 }
43062                         }
43063                 }
43064     }
43065 });
43066 /*
43067  * Based on:
43068  * Ext JS Library 1.1.1
43069  * Copyright(c) 2006-2007, Ext JS, LLC.
43070  *
43071  * Originally Released Under LGPL - original licence link has changed is not relivant.
43072  *
43073  * Fork - LGPL
43074  * <script type="text/javascript">
43075  */
43076  
43077 /**
43078  * @class Roo.LayoutManager
43079  * @extends Roo.util.Observable
43080  * Base class for layout managers.
43081  */
43082 Roo.LayoutManager = function(container, config){
43083     Roo.LayoutManager.superclass.constructor.call(this);
43084     this.el = Roo.get(container);
43085     // ie scrollbar fix
43086     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43087         document.body.scroll = "no";
43088     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43089         this.el.position('relative');
43090     }
43091     this.id = this.el.id;
43092     this.el.addClass("x-layout-container");
43093     /** false to disable window resize monitoring @type Boolean */
43094     this.monitorWindowResize = true;
43095     this.regions = {};
43096     this.addEvents({
43097         /**
43098          * @event layout
43099          * Fires when a layout is performed. 
43100          * @param {Roo.LayoutManager} this
43101          */
43102         "layout" : true,
43103         /**
43104          * @event regionresized
43105          * Fires when the user resizes a region. 
43106          * @param {Roo.LayoutRegion} region The resized region
43107          * @param {Number} newSize The new size (width for east/west, height for north/south)
43108          */
43109         "regionresized" : true,
43110         /**
43111          * @event regioncollapsed
43112          * Fires when a region is collapsed. 
43113          * @param {Roo.LayoutRegion} region The collapsed region
43114          */
43115         "regioncollapsed" : true,
43116         /**
43117          * @event regionexpanded
43118          * Fires when a region is expanded.  
43119          * @param {Roo.LayoutRegion} region The expanded region
43120          */
43121         "regionexpanded" : true
43122     });
43123     this.updating = false;
43124     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43125 };
43126
43127 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43128     /**
43129      * Returns true if this layout is currently being updated
43130      * @return {Boolean}
43131      */
43132     isUpdating : function(){
43133         return this.updating; 
43134     },
43135     
43136     /**
43137      * Suspend the LayoutManager from doing auto-layouts while
43138      * making multiple add or remove calls
43139      */
43140     beginUpdate : function(){
43141         this.updating = true;    
43142     },
43143     
43144     /**
43145      * Restore auto-layouts and optionally disable the manager from performing a layout
43146      * @param {Boolean} noLayout true to disable a layout update 
43147      */
43148     endUpdate : function(noLayout){
43149         this.updating = false;
43150         if(!noLayout){
43151             this.layout();
43152         }    
43153     },
43154     
43155     layout: function(){
43156         
43157     },
43158     
43159     onRegionResized : function(region, newSize){
43160         this.fireEvent("regionresized", region, newSize);
43161         this.layout();
43162     },
43163     
43164     onRegionCollapsed : function(region){
43165         this.fireEvent("regioncollapsed", region);
43166     },
43167     
43168     onRegionExpanded : function(region){
43169         this.fireEvent("regionexpanded", region);
43170     },
43171         
43172     /**
43173      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43174      * performs box-model adjustments.
43175      * @return {Object} The size as an object {width: (the width), height: (the height)}
43176      */
43177     getViewSize : function(){
43178         var size;
43179         if(this.el.dom != document.body){
43180             size = this.el.getSize();
43181         }else{
43182             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43183         }
43184         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43185         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43186         return size;
43187     },
43188     
43189     /**
43190      * Returns the Element this layout is bound to.
43191      * @return {Roo.Element}
43192      */
43193     getEl : function(){
43194         return this.el;
43195     },
43196     
43197     /**
43198      * Returns the specified region.
43199      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43200      * @return {Roo.LayoutRegion}
43201      */
43202     getRegion : function(target){
43203         return this.regions[target.toLowerCase()];
43204     },
43205     
43206     onWindowResize : function(){
43207         if(this.monitorWindowResize){
43208             this.layout();
43209         }
43210     }
43211 });/*
43212  * Based on:
43213  * Ext JS Library 1.1.1
43214  * Copyright(c) 2006-2007, Ext JS, LLC.
43215  *
43216  * Originally Released Under LGPL - original licence link has changed is not relivant.
43217  *
43218  * Fork - LGPL
43219  * <script type="text/javascript">
43220  */
43221 /**
43222  * @class Roo.BorderLayout
43223  * @extends Roo.LayoutManager
43224  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43225  * please see: <br><br>
43226  * <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>
43227  * <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>
43228  * Example:
43229  <pre><code>
43230  var layout = new Roo.BorderLayout(document.body, {
43231     north: {
43232         initialSize: 25,
43233         titlebar: false
43234     },
43235     west: {
43236         split:true,
43237         initialSize: 200,
43238         minSize: 175,
43239         maxSize: 400,
43240         titlebar: true,
43241         collapsible: true
43242     },
43243     east: {
43244         split:true,
43245         initialSize: 202,
43246         minSize: 175,
43247         maxSize: 400,
43248         titlebar: true,
43249         collapsible: true
43250     },
43251     south: {
43252         split:true,
43253         initialSize: 100,
43254         minSize: 100,
43255         maxSize: 200,
43256         titlebar: true,
43257         collapsible: true
43258     },
43259     center: {
43260         titlebar: true,
43261         autoScroll:true,
43262         resizeTabs: true,
43263         minTabWidth: 50,
43264         preferredTabWidth: 150
43265     }
43266 });
43267
43268 // shorthand
43269 var CP = Roo.ContentPanel;
43270
43271 layout.beginUpdate();
43272 layout.add("north", new CP("north", "North"));
43273 layout.add("south", new CP("south", {title: "South", closable: true}));
43274 layout.add("west", new CP("west", {title: "West"}));
43275 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43276 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43277 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43278 layout.getRegion("center").showPanel("center1");
43279 layout.endUpdate();
43280 </code></pre>
43281
43282 <b>The container the layout is rendered into can be either the body element or any other element.
43283 If it is not the body element, the container needs to either be an absolute positioned element,
43284 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43285 the container size if it is not the body element.</b>
43286
43287 * @constructor
43288 * Create a new BorderLayout
43289 * @param {String/HTMLElement/Element} container The container this layout is bound to
43290 * @param {Object} config Configuration options
43291  */
43292 Roo.BorderLayout = function(container, config){
43293     config = config || {};
43294     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43295     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43296     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43297         var target = this.factory.validRegions[i];
43298         if(config[target]){
43299             this.addRegion(target, config[target]);
43300         }
43301     }
43302 };
43303
43304 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43305     /**
43306      * Creates and adds a new region if it doesn't already exist.
43307      * @param {String} target The target region key (north, south, east, west or center).
43308      * @param {Object} config The regions config object
43309      * @return {BorderLayoutRegion} The new region
43310      */
43311     addRegion : function(target, config){
43312         if(!this.regions[target]){
43313             var r = this.factory.create(target, this, config);
43314             this.bindRegion(target, r);
43315         }
43316         return this.regions[target];
43317     },
43318
43319     // private (kinda)
43320     bindRegion : function(name, r){
43321         this.regions[name] = r;
43322         r.on("visibilitychange", this.layout, this);
43323         r.on("paneladded", this.layout, this);
43324         r.on("panelremoved", this.layout, this);
43325         r.on("invalidated", this.layout, this);
43326         r.on("resized", this.onRegionResized, this);
43327         r.on("collapsed", this.onRegionCollapsed, this);
43328         r.on("expanded", this.onRegionExpanded, this);
43329     },
43330
43331     /**
43332      * Performs a layout update.
43333      */
43334     layout : function(){
43335         if(this.updating) return;
43336         var size = this.getViewSize();
43337         var w = size.width;
43338         var h = size.height;
43339         var centerW = w;
43340         var centerH = h;
43341         var centerY = 0;
43342         var centerX = 0;
43343         //var x = 0, y = 0;
43344
43345         var rs = this.regions;
43346         var north = rs["north"];
43347         var south = rs["south"]; 
43348         var west = rs["west"];
43349         var east = rs["east"];
43350         var center = rs["center"];
43351         //if(this.hideOnLayout){ // not supported anymore
43352             //c.el.setStyle("display", "none");
43353         //}
43354         if(north && north.isVisible()){
43355             var b = north.getBox();
43356             var m = north.getMargins();
43357             b.width = w - (m.left+m.right);
43358             b.x = m.left;
43359             b.y = m.top;
43360             centerY = b.height + b.y + m.bottom;
43361             centerH -= centerY;
43362             north.updateBox(this.safeBox(b));
43363         }
43364         if(south && south.isVisible()){
43365             var b = south.getBox();
43366             var m = south.getMargins();
43367             b.width = w - (m.left+m.right);
43368             b.x = m.left;
43369             var totalHeight = (b.height + m.top + m.bottom);
43370             b.y = h - totalHeight + m.top;
43371             centerH -= totalHeight;
43372             south.updateBox(this.safeBox(b));
43373         }
43374         if(west && west.isVisible()){
43375             var b = west.getBox();
43376             var m = west.getMargins();
43377             b.height = centerH - (m.top+m.bottom);
43378             b.x = m.left;
43379             b.y = centerY + m.top;
43380             var totalWidth = (b.width + m.left + m.right);
43381             centerX += totalWidth;
43382             centerW -= totalWidth;
43383             west.updateBox(this.safeBox(b));
43384         }
43385         if(east && east.isVisible()){
43386             var b = east.getBox();
43387             var m = east.getMargins();
43388             b.height = centerH - (m.top+m.bottom);
43389             var totalWidth = (b.width + m.left + m.right);
43390             b.x = w - totalWidth + m.left;
43391             b.y = centerY + m.top;
43392             centerW -= totalWidth;
43393             east.updateBox(this.safeBox(b));
43394         }
43395         if(center){
43396             var m = center.getMargins();
43397             var centerBox = {
43398                 x: centerX + m.left,
43399                 y: centerY + m.top,
43400                 width: centerW - (m.left+m.right),
43401                 height: centerH - (m.top+m.bottom)
43402             };
43403             //if(this.hideOnLayout){
43404                 //center.el.setStyle("display", "block");
43405             //}
43406             center.updateBox(this.safeBox(centerBox));
43407         }
43408         this.el.repaint();
43409         this.fireEvent("layout", this);
43410     },
43411
43412     // private
43413     safeBox : function(box){
43414         box.width = Math.max(0, box.width);
43415         box.height = Math.max(0, box.height);
43416         return box;
43417     },
43418
43419     /**
43420      * Adds a ContentPanel (or subclass) to this layout.
43421      * @param {String} target The target region key (north, south, east, west or center).
43422      * @param {Roo.ContentPanel} panel The panel to add
43423      * @return {Roo.ContentPanel} The added panel
43424      */
43425     add : function(target, panel){
43426          
43427         target = target.toLowerCase();
43428         return this.regions[target].add(panel);
43429     },
43430
43431     /**
43432      * Remove a ContentPanel (or subclass) to this layout.
43433      * @param {String} target The target region key (north, south, east, west or center).
43434      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43435      * @return {Roo.ContentPanel} The removed panel
43436      */
43437     remove : function(target, panel){
43438         target = target.toLowerCase();
43439         return this.regions[target].remove(panel);
43440     },
43441
43442     /**
43443      * Searches all regions for a panel with the specified id
43444      * @param {String} panelId
43445      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43446      */
43447     findPanel : function(panelId){
43448         var rs = this.regions;
43449         for(var target in rs){
43450             if(typeof rs[target] != "function"){
43451                 var p = rs[target].getPanel(panelId);
43452                 if(p){
43453                     return p;
43454                 }
43455             }
43456         }
43457         return null;
43458     },
43459
43460     /**
43461      * Searches all regions for a panel with the specified id and activates (shows) it.
43462      * @param {String/ContentPanel} panelId The panels id or the panel itself
43463      * @return {Roo.ContentPanel} The shown panel or null
43464      */
43465     showPanel : function(panelId) {
43466       var rs = this.regions;
43467       for(var target in rs){
43468          var r = rs[target];
43469          if(typeof r != "function"){
43470             if(r.hasPanel(panelId)){
43471                return r.showPanel(panelId);
43472             }
43473          }
43474       }
43475       return null;
43476    },
43477
43478    /**
43479      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43480      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43481      */
43482     restoreState : function(provider){
43483         if(!provider){
43484             provider = Roo.state.Manager;
43485         }
43486         var sm = new Roo.LayoutStateManager();
43487         sm.init(this, provider);
43488     },
43489
43490     /**
43491      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43492      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43493      * a valid ContentPanel config object.  Example:
43494      * <pre><code>
43495 // Create the main layout
43496 var layout = new Roo.BorderLayout('main-ct', {
43497     west: {
43498         split:true,
43499         minSize: 175,
43500         titlebar: true
43501     },
43502     center: {
43503         title:'Components'
43504     }
43505 }, 'main-ct');
43506
43507 // Create and add multiple ContentPanels at once via configs
43508 layout.batchAdd({
43509    west: {
43510        id: 'source-files',
43511        autoCreate:true,
43512        title:'Ext Source Files',
43513        autoScroll:true,
43514        fitToFrame:true
43515    },
43516    center : {
43517        el: cview,
43518        autoScroll:true,
43519        fitToFrame:true,
43520        toolbar: tb,
43521        resizeEl:'cbody'
43522    }
43523 });
43524 </code></pre>
43525      * @param {Object} regions An object containing ContentPanel configs by region name
43526      */
43527     batchAdd : function(regions){
43528         this.beginUpdate();
43529         for(var rname in regions){
43530             var lr = this.regions[rname];
43531             if(lr){
43532                 this.addTypedPanels(lr, regions[rname]);
43533             }
43534         }
43535         this.endUpdate();
43536     },
43537
43538     // private
43539     addTypedPanels : function(lr, ps){
43540         if(typeof ps == 'string'){
43541             lr.add(new Roo.ContentPanel(ps));
43542         }
43543         else if(ps instanceof Array){
43544             for(var i =0, len = ps.length; i < len; i++){
43545                 this.addTypedPanels(lr, ps[i]);
43546             }
43547         }
43548         else if(!ps.events){ // raw config?
43549             var el = ps.el;
43550             delete ps.el; // prevent conflict
43551             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43552         }
43553         else {  // panel object assumed!
43554             lr.add(ps);
43555         }
43556     },
43557     /**
43558      * Adds a xtype elements to the layout.
43559      * <pre><code>
43560
43561 layout.addxtype({
43562        xtype : 'ContentPanel',
43563        region: 'west',
43564        items: [ .... ]
43565    }
43566 );
43567
43568 layout.addxtype({
43569         xtype : 'NestedLayoutPanel',
43570         region: 'west',
43571         layout: {
43572            center: { },
43573            west: { }   
43574         },
43575         items : [ ... list of content panels or nested layout panels.. ]
43576    }
43577 );
43578 </code></pre>
43579      * @param {Object} cfg Xtype definition of item to add.
43580      */
43581     addxtype : function(cfg)
43582     {
43583         // basically accepts a pannel...
43584         // can accept a layout region..!?!?
43585        // console.log('BorderLayout add ' + cfg.xtype)
43586         
43587         if (!cfg.xtype.match(/Panel$/)) {
43588             return false;
43589         }
43590         var ret = false;
43591         var region = cfg.region;
43592         delete cfg.region;
43593         
43594           
43595         var xitems = [];
43596         if (cfg.items) {
43597             xitems = cfg.items;
43598             delete cfg.items;
43599         }
43600         
43601         
43602         switch(cfg.xtype) 
43603         {
43604             case 'ContentPanel':  // ContentPanel (el, cfg)
43605             case 'ScrollPanel':  // ContentPanel (el, cfg)
43606                 if(cfg.autoCreate) {
43607                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43608                 } else {
43609                     var el = this.el.createChild();
43610                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43611                 }
43612                 
43613                 this.add(region, ret);
43614                 break;
43615             
43616             
43617             case 'TreePanel': // our new panel!
43618                 cfg.el = this.el.createChild();
43619                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43620                 this.add(region, ret);
43621                 break;
43622             
43623             case 'NestedLayoutPanel': 
43624                 // create a new Layout (which is  a Border Layout...
43625                 var el = this.el.createChild();
43626                 var clayout = cfg.layout;
43627                 delete cfg.layout;
43628                 clayout.items   = clayout.items  || [];
43629                 // replace this exitems with the clayout ones..
43630                 xitems = clayout.items;
43631                  
43632                 
43633                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43634                     cfg.background = false;
43635                 }
43636                 var layout = new Roo.BorderLayout(el, clayout);
43637                 
43638                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43639                 //console.log('adding nested layout panel '  + cfg.toSource());
43640                 this.add(region, ret);
43641                 
43642                 break;
43643                 
43644             case 'GridPanel': 
43645             
43646                 // needs grid and region
43647                 
43648                 //var el = this.getRegion(region).el.createChild();
43649                 var el = this.el.createChild();
43650                 // create the grid first...
43651                 
43652                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43653                 delete cfg.grid;
43654                 if (region == 'center' && this.active ) {
43655                     cfg.background = false;
43656                 }
43657                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43658                 
43659                 this.add(region, ret);
43660                 if (cfg.background) {
43661                     ret.on('activate', function(gp) {
43662                         if (!gp.grid.rendered) {
43663                             gp.grid.render();
43664                         }
43665                     });
43666                 } else {
43667                     grid.render();
43668                 }
43669                 break;
43670            
43671                
43672                 
43673                 
43674             default: 
43675                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43676                 return;
43677              // GridPanel (grid, cfg)
43678             
43679         }
43680         this.beginUpdate();
43681         // add children..
43682         Roo.each(xitems, function(i)  {
43683             ret.addxtype(i);
43684         });
43685         this.endUpdate();
43686         return ret;
43687         
43688     }
43689 });
43690
43691 /**
43692  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43693  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43694  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43695  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43696  * <pre><code>
43697 // shorthand
43698 var CP = Roo.ContentPanel;
43699
43700 var layout = Roo.BorderLayout.create({
43701     north: {
43702         initialSize: 25,
43703         titlebar: false,
43704         panels: [new CP("north", "North")]
43705     },
43706     west: {
43707         split:true,
43708         initialSize: 200,
43709         minSize: 175,
43710         maxSize: 400,
43711         titlebar: true,
43712         collapsible: true,
43713         panels: [new CP("west", {title: "West"})]
43714     },
43715     east: {
43716         split:true,
43717         initialSize: 202,
43718         minSize: 175,
43719         maxSize: 400,
43720         titlebar: true,
43721         collapsible: true,
43722         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43723     },
43724     south: {
43725         split:true,
43726         initialSize: 100,
43727         minSize: 100,
43728         maxSize: 200,
43729         titlebar: true,
43730         collapsible: true,
43731         panels: [new CP("south", {title: "South", closable: true})]
43732     },
43733     center: {
43734         titlebar: true,
43735         autoScroll:true,
43736         resizeTabs: true,
43737         minTabWidth: 50,
43738         preferredTabWidth: 150,
43739         panels: [
43740             new CP("center1", {title: "Close Me", closable: true}),
43741             new CP("center2", {title: "Center Panel", closable: false})
43742         ]
43743     }
43744 }, document.body);
43745
43746 layout.getRegion("center").showPanel("center1");
43747 </code></pre>
43748  * @param config
43749  * @param targetEl
43750  */
43751 Roo.BorderLayout.create = function(config, targetEl){
43752     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43753     layout.beginUpdate();
43754     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43755     for(var j = 0, jlen = regions.length; j < jlen; j++){
43756         var lr = regions[j];
43757         if(layout.regions[lr] && config[lr].panels){
43758             var r = layout.regions[lr];
43759             var ps = config[lr].panels;
43760             layout.addTypedPanels(r, ps);
43761         }
43762     }
43763     layout.endUpdate();
43764     return layout;
43765 };
43766
43767 // private
43768 Roo.BorderLayout.RegionFactory = {
43769     // private
43770     validRegions : ["north","south","east","west","center"],
43771
43772     // private
43773     create : function(target, mgr, config){
43774         target = target.toLowerCase();
43775         if(config.lightweight || config.basic){
43776             return new Roo.BasicLayoutRegion(mgr, config, target);
43777         }
43778         switch(target){
43779             case "north":
43780                 return new Roo.NorthLayoutRegion(mgr, config);
43781             case "south":
43782                 return new Roo.SouthLayoutRegion(mgr, config);
43783             case "east":
43784                 return new Roo.EastLayoutRegion(mgr, config);
43785             case "west":
43786                 return new Roo.WestLayoutRegion(mgr, config);
43787             case "center":
43788                 return new Roo.CenterLayoutRegion(mgr, config);
43789         }
43790         throw 'Layout region "'+target+'" not supported.';
43791     }
43792 };/*
43793  * Based on:
43794  * Ext JS Library 1.1.1
43795  * Copyright(c) 2006-2007, Ext JS, LLC.
43796  *
43797  * Originally Released Under LGPL - original licence link has changed is not relivant.
43798  *
43799  * Fork - LGPL
43800  * <script type="text/javascript">
43801  */
43802  
43803 /**
43804  * @class Roo.BasicLayoutRegion
43805  * @extends Roo.util.Observable
43806  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43807  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43808  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43809  */
43810 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43811     this.mgr = mgr;
43812     this.position  = pos;
43813     this.events = {
43814         /**
43815          * @scope Roo.BasicLayoutRegion
43816          */
43817         
43818         /**
43819          * @event beforeremove
43820          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43821          * @param {Roo.LayoutRegion} this
43822          * @param {Roo.ContentPanel} panel The panel
43823          * @param {Object} e The cancel event object
43824          */
43825         "beforeremove" : true,
43826         /**
43827          * @event invalidated
43828          * Fires when the layout for this region is changed.
43829          * @param {Roo.LayoutRegion} this
43830          */
43831         "invalidated" : true,
43832         /**
43833          * @event visibilitychange
43834          * Fires when this region is shown or hidden 
43835          * @param {Roo.LayoutRegion} this
43836          * @param {Boolean} visibility true or false
43837          */
43838         "visibilitychange" : true,
43839         /**
43840          * @event paneladded
43841          * Fires when a panel is added. 
43842          * @param {Roo.LayoutRegion} this
43843          * @param {Roo.ContentPanel} panel The panel
43844          */
43845         "paneladded" : true,
43846         /**
43847          * @event panelremoved
43848          * Fires when a panel is removed. 
43849          * @param {Roo.LayoutRegion} this
43850          * @param {Roo.ContentPanel} panel The panel
43851          */
43852         "panelremoved" : true,
43853         /**
43854          * @event collapsed
43855          * Fires when this region is collapsed.
43856          * @param {Roo.LayoutRegion} this
43857          */
43858         "collapsed" : true,
43859         /**
43860          * @event expanded
43861          * Fires when this region is expanded.
43862          * @param {Roo.LayoutRegion} this
43863          */
43864         "expanded" : true,
43865         /**
43866          * @event slideshow
43867          * Fires when this region is slid into view.
43868          * @param {Roo.LayoutRegion} this
43869          */
43870         "slideshow" : true,
43871         /**
43872          * @event slidehide
43873          * Fires when this region slides out of view. 
43874          * @param {Roo.LayoutRegion} this
43875          */
43876         "slidehide" : true,
43877         /**
43878          * @event panelactivated
43879          * Fires when a panel is activated. 
43880          * @param {Roo.LayoutRegion} this
43881          * @param {Roo.ContentPanel} panel The activated panel
43882          */
43883         "panelactivated" : true,
43884         /**
43885          * @event resized
43886          * Fires when the user resizes this region. 
43887          * @param {Roo.LayoutRegion} this
43888          * @param {Number} newSize The new size (width for east/west, height for north/south)
43889          */
43890         "resized" : true
43891     };
43892     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43893     this.panels = new Roo.util.MixedCollection();
43894     this.panels.getKey = this.getPanelId.createDelegate(this);
43895     this.box = null;
43896     this.activePanel = null;
43897     // ensure listeners are added...
43898     
43899     if (config.listeners || config.events) {
43900         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43901             listeners : config.listeners || {},
43902             events : config.events || {}
43903         });
43904     }
43905     
43906     if(skipConfig !== true){
43907         this.applyConfig(config);
43908     }
43909 };
43910
43911 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43912     getPanelId : function(p){
43913         return p.getId();
43914     },
43915     
43916     applyConfig : function(config){
43917         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43918         this.config = config;
43919         
43920     },
43921     
43922     /**
43923      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43924      * the width, for horizontal (north, south) the height.
43925      * @param {Number} newSize The new width or height
43926      */
43927     resizeTo : function(newSize){
43928         var el = this.el ? this.el :
43929                  (this.activePanel ? this.activePanel.getEl() : null);
43930         if(el){
43931             switch(this.position){
43932                 case "east":
43933                 case "west":
43934                     el.setWidth(newSize);
43935                     this.fireEvent("resized", this, newSize);
43936                 break;
43937                 case "north":
43938                 case "south":
43939                     el.setHeight(newSize);
43940                     this.fireEvent("resized", this, newSize);
43941                 break;                
43942             }
43943         }
43944     },
43945     
43946     getBox : function(){
43947         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43948     },
43949     
43950     getMargins : function(){
43951         return this.margins;
43952     },
43953     
43954     updateBox : function(box){
43955         this.box = box;
43956         var el = this.activePanel.getEl();
43957         el.dom.style.left = box.x + "px";
43958         el.dom.style.top = box.y + "px";
43959         this.activePanel.setSize(box.width, box.height);
43960     },
43961     
43962     /**
43963      * Returns the container element for this region.
43964      * @return {Roo.Element}
43965      */
43966     getEl : function(){
43967         return this.activePanel;
43968     },
43969     
43970     /**
43971      * Returns true if this region is currently visible.
43972      * @return {Boolean}
43973      */
43974     isVisible : function(){
43975         return this.activePanel ? true : false;
43976     },
43977     
43978     setActivePanel : function(panel){
43979         panel = this.getPanel(panel);
43980         if(this.activePanel && this.activePanel != panel){
43981             this.activePanel.setActiveState(false);
43982             this.activePanel.getEl().setLeftTop(-10000,-10000);
43983         }
43984         this.activePanel = panel;
43985         panel.setActiveState(true);
43986         if(this.box){
43987             panel.setSize(this.box.width, this.box.height);
43988         }
43989         this.fireEvent("panelactivated", this, panel);
43990         this.fireEvent("invalidated");
43991     },
43992     
43993     /**
43994      * Show the specified panel.
43995      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43996      * @return {Roo.ContentPanel} The shown panel or null
43997      */
43998     showPanel : function(panel){
43999         if(panel = this.getPanel(panel)){
44000             this.setActivePanel(panel);
44001         }
44002         return panel;
44003     },
44004     
44005     /**
44006      * Get the active panel for this region.
44007      * @return {Roo.ContentPanel} The active panel or null
44008      */
44009     getActivePanel : function(){
44010         return this.activePanel;
44011     },
44012     
44013     /**
44014      * Add the passed ContentPanel(s)
44015      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44016      * @return {Roo.ContentPanel} The panel added (if only one was added)
44017      */
44018     add : function(panel){
44019         if(arguments.length > 1){
44020             for(var i = 0, len = arguments.length; i < len; i++) {
44021                 this.add(arguments[i]);
44022             }
44023             return null;
44024         }
44025         if(this.hasPanel(panel)){
44026             this.showPanel(panel);
44027             return panel;
44028         }
44029         var el = panel.getEl();
44030         if(el.dom.parentNode != this.mgr.el.dom){
44031             this.mgr.el.dom.appendChild(el.dom);
44032         }
44033         if(panel.setRegion){
44034             panel.setRegion(this);
44035         }
44036         this.panels.add(panel);
44037         el.setStyle("position", "absolute");
44038         if(!panel.background){
44039             this.setActivePanel(panel);
44040             if(this.config.initialSize && this.panels.getCount()==1){
44041                 this.resizeTo(this.config.initialSize);
44042             }
44043         }
44044         this.fireEvent("paneladded", this, panel);
44045         return panel;
44046     },
44047     
44048     /**
44049      * Returns true if the panel is in this region.
44050      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44051      * @return {Boolean}
44052      */
44053     hasPanel : function(panel){
44054         if(typeof panel == "object"){ // must be panel obj
44055             panel = panel.getId();
44056         }
44057         return this.getPanel(panel) ? true : false;
44058     },
44059     
44060     /**
44061      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44062      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44063      * @param {Boolean} preservePanel Overrides the config preservePanel option
44064      * @return {Roo.ContentPanel} The panel that was removed
44065      */
44066     remove : function(panel, preservePanel){
44067         panel = this.getPanel(panel);
44068         if(!panel){
44069             return null;
44070         }
44071         var e = {};
44072         this.fireEvent("beforeremove", this, panel, e);
44073         if(e.cancel === true){
44074             return null;
44075         }
44076         var panelId = panel.getId();
44077         this.panels.removeKey(panelId);
44078         return panel;
44079     },
44080     
44081     /**
44082      * Returns the panel specified or null if it's not in this region.
44083      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44084      * @return {Roo.ContentPanel}
44085      */
44086     getPanel : function(id){
44087         if(typeof id == "object"){ // must be panel obj
44088             return id;
44089         }
44090         return this.panels.get(id);
44091     },
44092     
44093     /**
44094      * Returns this regions position (north/south/east/west/center).
44095      * @return {String} 
44096      */
44097     getPosition: function(){
44098         return this.position;    
44099     }
44100 });/*
44101  * Based on:
44102  * Ext JS Library 1.1.1
44103  * Copyright(c) 2006-2007, Ext JS, LLC.
44104  *
44105  * Originally Released Under LGPL - original licence link has changed is not relivant.
44106  *
44107  * Fork - LGPL
44108  * <script type="text/javascript">
44109  */
44110  
44111 /**
44112  * @class Roo.LayoutRegion
44113  * @extends Roo.BasicLayoutRegion
44114  * This class represents a region in a layout manager.
44115  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44116  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44117  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44118  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44119  * @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})
44120  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44121  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44122  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44123  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44124  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44125  * @cfg {String} title The title for the region (overrides panel titles)
44126  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44127  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44128  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44129  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44130  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44131  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44132  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44133  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44134  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44135  * @cfg {Boolean} showPin True to show a pin button
44136 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44137 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44138 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44139 * @cfg {Number} width  For East/West panels
44140 * @cfg {Number} height For North/South panels
44141 * @cfg {Boolean} split To show the splitter
44142  */
44143 Roo.LayoutRegion = function(mgr, config, pos){
44144     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44145     var dh = Roo.DomHelper;
44146     /** This region's container element 
44147     * @type Roo.Element */
44148     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44149     /** This region's title element 
44150     * @type Roo.Element */
44151
44152     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44153         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44154         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44155     ]}, true);
44156     this.titleEl.enableDisplayMode();
44157     /** This region's title text element 
44158     * @type HTMLElement */
44159     this.titleTextEl = this.titleEl.dom.firstChild;
44160     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44161     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44162     this.closeBtn.enableDisplayMode();
44163     this.closeBtn.on("click", this.closeClicked, this);
44164     this.closeBtn.hide();
44165
44166     this.createBody(config);
44167     this.visible = true;
44168     this.collapsed = false;
44169
44170     if(config.hideWhenEmpty){
44171         this.hide();
44172         this.on("paneladded", this.validateVisibility, this);
44173         this.on("panelremoved", this.validateVisibility, this);
44174     }
44175     this.applyConfig(config);
44176 };
44177
44178 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44179
44180     createBody : function(){
44181         /** This region's body element 
44182         * @type Roo.Element */
44183         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44184     },
44185
44186     applyConfig : function(c){
44187         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44188             var dh = Roo.DomHelper;
44189             if(c.titlebar !== false){
44190                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44191                 this.collapseBtn.on("click", this.collapse, this);
44192                 this.collapseBtn.enableDisplayMode();
44193
44194                 if(c.showPin === true || this.showPin){
44195                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44196                     this.stickBtn.enableDisplayMode();
44197                     this.stickBtn.on("click", this.expand, this);
44198                     this.stickBtn.hide();
44199                 }
44200             }
44201             /** This region's collapsed element
44202             * @type Roo.Element */
44203             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44204                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44205             ]}, true);
44206             if(c.floatable !== false){
44207                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44208                this.collapsedEl.on("click", this.collapseClick, this);
44209             }
44210
44211             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44212                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44213                    id: "message", unselectable: "on", style:{"float":"left"}});
44214                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44215              }
44216             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44217             this.expandBtn.on("click", this.expand, this);
44218         }
44219         if(this.collapseBtn){
44220             this.collapseBtn.setVisible(c.collapsible == true);
44221         }
44222         this.cmargins = c.cmargins || this.cmargins ||
44223                          (this.position == "west" || this.position == "east" ?
44224                              {top: 0, left: 2, right:2, bottom: 0} :
44225                              {top: 2, left: 0, right:0, bottom: 2});
44226         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44227         this.bottomTabs = c.tabPosition != "top";
44228         this.autoScroll = c.autoScroll || false;
44229         if(this.autoScroll){
44230             this.bodyEl.setStyle("overflow", "auto");
44231         }else{
44232             this.bodyEl.setStyle("overflow", "hidden");
44233         }
44234         //if(c.titlebar !== false){
44235             if((!c.titlebar && !c.title) || c.titlebar === false){
44236                 this.titleEl.hide();
44237             }else{
44238                 this.titleEl.show();
44239                 if(c.title){
44240                     this.titleTextEl.innerHTML = c.title;
44241                 }
44242             }
44243         //}
44244         this.duration = c.duration || .30;
44245         this.slideDuration = c.slideDuration || .45;
44246         this.config = c;
44247         if(c.collapsed){
44248             this.collapse(true);
44249         }
44250         if(c.hidden){
44251             this.hide();
44252         }
44253     },
44254     /**
44255      * Returns true if this region is currently visible.
44256      * @return {Boolean}
44257      */
44258     isVisible : function(){
44259         return this.visible;
44260     },
44261
44262     /**
44263      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44264      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44265      */
44266     setCollapsedTitle : function(title){
44267         title = title || "&#160;";
44268         if(this.collapsedTitleTextEl){
44269             this.collapsedTitleTextEl.innerHTML = title;
44270         }
44271     },
44272
44273     getBox : function(){
44274         var b;
44275         if(!this.collapsed){
44276             b = this.el.getBox(false, true);
44277         }else{
44278             b = this.collapsedEl.getBox(false, true);
44279         }
44280         return b;
44281     },
44282
44283     getMargins : function(){
44284         return this.collapsed ? this.cmargins : this.margins;
44285     },
44286
44287     highlight : function(){
44288         this.el.addClass("x-layout-panel-dragover");
44289     },
44290
44291     unhighlight : function(){
44292         this.el.removeClass("x-layout-panel-dragover");
44293     },
44294
44295     updateBox : function(box){
44296         this.box = box;
44297         if(!this.collapsed){
44298             this.el.dom.style.left = box.x + "px";
44299             this.el.dom.style.top = box.y + "px";
44300             this.updateBody(box.width, box.height);
44301         }else{
44302             this.collapsedEl.dom.style.left = box.x + "px";
44303             this.collapsedEl.dom.style.top = box.y + "px";
44304             this.collapsedEl.setSize(box.width, box.height);
44305         }
44306         if(this.tabs){
44307             this.tabs.autoSizeTabs();
44308         }
44309     },
44310
44311     updateBody : function(w, h){
44312         if(w !== null){
44313             this.el.setWidth(w);
44314             w -= this.el.getBorderWidth("rl");
44315             if(this.config.adjustments){
44316                 w += this.config.adjustments[0];
44317             }
44318         }
44319         if(h !== null){
44320             this.el.setHeight(h);
44321             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44322             h -= this.el.getBorderWidth("tb");
44323             if(this.config.adjustments){
44324                 h += this.config.adjustments[1];
44325             }
44326             this.bodyEl.setHeight(h);
44327             if(this.tabs){
44328                 h = this.tabs.syncHeight(h);
44329             }
44330         }
44331         if(this.panelSize){
44332             w = w !== null ? w : this.panelSize.width;
44333             h = h !== null ? h : this.panelSize.height;
44334         }
44335         if(this.activePanel){
44336             var el = this.activePanel.getEl();
44337             w = w !== null ? w : el.getWidth();
44338             h = h !== null ? h : el.getHeight();
44339             this.panelSize = {width: w, height: h};
44340             this.activePanel.setSize(w, h);
44341         }
44342         if(Roo.isIE && this.tabs){
44343             this.tabs.el.repaint();
44344         }
44345     },
44346
44347     /**
44348      * Returns the container element for this region.
44349      * @return {Roo.Element}
44350      */
44351     getEl : function(){
44352         return this.el;
44353     },
44354
44355     /**
44356      * Hides this region.
44357      */
44358     hide : function(){
44359         if(!this.collapsed){
44360             this.el.dom.style.left = "-2000px";
44361             this.el.hide();
44362         }else{
44363             this.collapsedEl.dom.style.left = "-2000px";
44364             this.collapsedEl.hide();
44365         }
44366         this.visible = false;
44367         this.fireEvent("visibilitychange", this, false);
44368     },
44369
44370     /**
44371      * Shows this region if it was previously hidden.
44372      */
44373     show : function(){
44374         if(!this.collapsed){
44375             this.el.show();
44376         }else{
44377             this.collapsedEl.show();
44378         }
44379         this.visible = true;
44380         this.fireEvent("visibilitychange", this, true);
44381     },
44382
44383     closeClicked : function(){
44384         if(this.activePanel){
44385             this.remove(this.activePanel);
44386         }
44387     },
44388
44389     collapseClick : function(e){
44390         if(this.isSlid){
44391            e.stopPropagation();
44392            this.slideIn();
44393         }else{
44394            e.stopPropagation();
44395            this.slideOut();
44396         }
44397     },
44398
44399     /**
44400      * Collapses this region.
44401      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44402      */
44403     collapse : function(skipAnim){
44404         if(this.collapsed) return;
44405         this.collapsed = true;
44406         if(this.split){
44407             this.split.el.hide();
44408         }
44409         if(this.config.animate && skipAnim !== true){
44410             this.fireEvent("invalidated", this);
44411             this.animateCollapse();
44412         }else{
44413             this.el.setLocation(-20000,-20000);
44414             this.el.hide();
44415             this.collapsedEl.show();
44416             this.fireEvent("collapsed", this);
44417             this.fireEvent("invalidated", this);
44418         }
44419     },
44420
44421     animateCollapse : function(){
44422         // overridden
44423     },
44424
44425     /**
44426      * Expands this region if it was previously collapsed.
44427      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44428      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44429      */
44430     expand : function(e, skipAnim){
44431         if(e) e.stopPropagation();
44432         if(!this.collapsed || this.el.hasActiveFx()) return;
44433         if(this.isSlid){
44434             this.afterSlideIn();
44435             skipAnim = true;
44436         }
44437         this.collapsed = false;
44438         if(this.config.animate && skipAnim !== true){
44439             this.animateExpand();
44440         }else{
44441             this.el.show();
44442             if(this.split){
44443                 this.split.el.show();
44444             }
44445             this.collapsedEl.setLocation(-2000,-2000);
44446             this.collapsedEl.hide();
44447             this.fireEvent("invalidated", this);
44448             this.fireEvent("expanded", this);
44449         }
44450     },
44451
44452     animateExpand : function(){
44453         // overridden
44454     },
44455
44456     initTabs : function(){
44457         this.bodyEl.setStyle("overflow", "hidden");
44458         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44459             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44460             disableTooltips: this.config.disableTabTips
44461         });
44462         if(this.config.hideTabs){
44463             ts.stripWrap.setDisplayed(false);
44464         }
44465         this.tabs = ts;
44466         ts.resizeTabs = this.config.resizeTabs === true;
44467         ts.minTabWidth = this.config.minTabWidth || 40;
44468         ts.maxTabWidth = this.config.maxTabWidth || 250;
44469         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44470         ts.monitorResize = false;
44471         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44472         ts.bodyEl.addClass('x-layout-tabs-body');
44473         this.panels.each(this.initPanelAsTab, this);
44474     },
44475
44476     initPanelAsTab : function(panel){
44477         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44478                     this.config.closeOnTab && panel.isClosable());
44479         if(panel.tabTip !== undefined){
44480             ti.setTooltip(panel.tabTip);
44481         }
44482         ti.on("activate", function(){
44483               this.setActivePanel(panel);
44484         }, this);
44485         if(this.config.closeOnTab){
44486             ti.on("beforeclose", function(t, e){
44487                 e.cancel = true;
44488                 this.remove(panel);
44489             }, this);
44490         }
44491         return ti;
44492     },
44493
44494     updatePanelTitle : function(panel, title){
44495         if(this.activePanel == panel){
44496             this.updateTitle(title);
44497         }
44498         if(this.tabs){
44499             var ti = this.tabs.getTab(panel.getEl().id);
44500             ti.setText(title);
44501             if(panel.tabTip !== undefined){
44502                 ti.setTooltip(panel.tabTip);
44503             }
44504         }
44505     },
44506
44507     updateTitle : function(title){
44508         if(this.titleTextEl && !this.config.title){
44509             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44510         }
44511     },
44512
44513     setActivePanel : function(panel){
44514         panel = this.getPanel(panel);
44515         if(this.activePanel && this.activePanel != panel){
44516             this.activePanel.setActiveState(false);
44517         }
44518         this.activePanel = panel;
44519         panel.setActiveState(true);
44520         if(this.panelSize){
44521             panel.setSize(this.panelSize.width, this.panelSize.height);
44522         }
44523         if(this.closeBtn){
44524             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44525         }
44526         this.updateTitle(panel.getTitle());
44527         if(this.tabs){
44528             this.fireEvent("invalidated", this);
44529         }
44530         this.fireEvent("panelactivated", this, panel);
44531     },
44532
44533     /**
44534      * Shows the specified panel.
44535      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44536      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44537      */
44538     showPanel : function(panel){
44539         if(panel = this.getPanel(panel)){
44540             if(this.tabs){
44541                 var tab = this.tabs.getTab(panel.getEl().id);
44542                 if(tab.isHidden()){
44543                     this.tabs.unhideTab(tab.id);
44544                 }
44545                 tab.activate();
44546             }else{
44547                 this.setActivePanel(panel);
44548             }
44549         }
44550         return panel;
44551     },
44552
44553     /**
44554      * Get the active panel for this region.
44555      * @return {Roo.ContentPanel} The active panel or null
44556      */
44557     getActivePanel : function(){
44558         return this.activePanel;
44559     },
44560
44561     validateVisibility : function(){
44562         if(this.panels.getCount() < 1){
44563             this.updateTitle("&#160;");
44564             this.closeBtn.hide();
44565             this.hide();
44566         }else{
44567             if(!this.isVisible()){
44568                 this.show();
44569             }
44570         }
44571     },
44572
44573     /**
44574      * Adds the passed ContentPanel(s) to this region.
44575      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44576      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44577      */
44578     add : function(panel){
44579         if(arguments.length > 1){
44580             for(var i = 0, len = arguments.length; i < len; i++) {
44581                 this.add(arguments[i]);
44582             }
44583             return null;
44584         }
44585         if(this.hasPanel(panel)){
44586             this.showPanel(panel);
44587             return panel;
44588         }
44589         panel.setRegion(this);
44590         this.panels.add(panel);
44591         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44592             this.bodyEl.dom.appendChild(panel.getEl().dom);
44593             if(panel.background !== true){
44594                 this.setActivePanel(panel);
44595             }
44596             this.fireEvent("paneladded", this, panel);
44597             return panel;
44598         }
44599         if(!this.tabs){
44600             this.initTabs();
44601         }else{
44602             this.initPanelAsTab(panel);
44603         }
44604         if(panel.background !== true){
44605             this.tabs.activate(panel.getEl().id);
44606         }
44607         this.fireEvent("paneladded", this, panel);
44608         return panel;
44609     },
44610
44611     /**
44612      * Hides the tab for the specified panel.
44613      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44614      */
44615     hidePanel : function(panel){
44616         if(this.tabs && (panel = this.getPanel(panel))){
44617             this.tabs.hideTab(panel.getEl().id);
44618         }
44619     },
44620
44621     /**
44622      * Unhides the tab for a previously hidden panel.
44623      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44624      */
44625     unhidePanel : function(panel){
44626         if(this.tabs && (panel = this.getPanel(panel))){
44627             this.tabs.unhideTab(panel.getEl().id);
44628         }
44629     },
44630
44631     clearPanels : function(){
44632         while(this.panels.getCount() > 0){
44633              this.remove(this.panels.first());
44634         }
44635     },
44636
44637     /**
44638      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44639      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44640      * @param {Boolean} preservePanel Overrides the config preservePanel option
44641      * @return {Roo.ContentPanel} The panel that was removed
44642      */
44643     remove : function(panel, preservePanel){
44644         panel = this.getPanel(panel);
44645         if(!panel){
44646             return null;
44647         }
44648         var e = {};
44649         this.fireEvent("beforeremove", this, panel, e);
44650         if(e.cancel === true){
44651             return null;
44652         }
44653         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44654         var panelId = panel.getId();
44655         this.panels.removeKey(panelId);
44656         if(preservePanel){
44657             document.body.appendChild(panel.getEl().dom);
44658         }
44659         if(this.tabs){
44660             this.tabs.removeTab(panel.getEl().id);
44661         }else if (!preservePanel){
44662             this.bodyEl.dom.removeChild(panel.getEl().dom);
44663         }
44664         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44665             var p = this.panels.first();
44666             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44667             tempEl.appendChild(p.getEl().dom);
44668             this.bodyEl.update("");
44669             this.bodyEl.dom.appendChild(p.getEl().dom);
44670             tempEl = null;
44671             this.updateTitle(p.getTitle());
44672             this.tabs = null;
44673             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44674             this.setActivePanel(p);
44675         }
44676         panel.setRegion(null);
44677         if(this.activePanel == panel){
44678             this.activePanel = null;
44679         }
44680         if(this.config.autoDestroy !== false && preservePanel !== true){
44681             try{panel.destroy();}catch(e){}
44682         }
44683         this.fireEvent("panelremoved", this, panel);
44684         return panel;
44685     },
44686
44687     /**
44688      * Returns the TabPanel component used by this region
44689      * @return {Roo.TabPanel}
44690      */
44691     getTabs : function(){
44692         return this.tabs;
44693     },
44694
44695     createTool : function(parentEl, className){
44696         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44697             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44698         btn.addClassOnOver("x-layout-tools-button-over");
44699         return btn;
44700     }
44701 });/*
44702  * Based on:
44703  * Ext JS Library 1.1.1
44704  * Copyright(c) 2006-2007, Ext JS, LLC.
44705  *
44706  * Originally Released Under LGPL - original licence link has changed is not relivant.
44707  *
44708  * Fork - LGPL
44709  * <script type="text/javascript">
44710  */
44711  
44712
44713
44714 /**
44715  * @class Roo.SplitLayoutRegion
44716  * @extends Roo.LayoutRegion
44717  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44718  */
44719 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44720     this.cursor = cursor;
44721     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44722 };
44723
44724 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44725     splitTip : "Drag to resize.",
44726     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44727     useSplitTips : false,
44728
44729     applyConfig : function(config){
44730         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44731         if(config.split){
44732             if(!this.split){
44733                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44734                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44735                 /** The SplitBar for this region 
44736                 * @type Roo.SplitBar */
44737                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44738                 this.split.on("moved", this.onSplitMove, this);
44739                 this.split.useShim = config.useShim === true;
44740                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44741                 if(this.useSplitTips){
44742                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44743                 }
44744                 if(config.collapsible){
44745                     this.split.el.on("dblclick", this.collapse,  this);
44746                 }
44747             }
44748             if(typeof config.minSize != "undefined"){
44749                 this.split.minSize = config.minSize;
44750             }
44751             if(typeof config.maxSize != "undefined"){
44752                 this.split.maxSize = config.maxSize;
44753             }
44754             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44755                 this.hideSplitter();
44756             }
44757         }
44758     },
44759
44760     getHMaxSize : function(){
44761          var cmax = this.config.maxSize || 10000;
44762          var center = this.mgr.getRegion("center");
44763          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44764     },
44765
44766     getVMaxSize : function(){
44767          var cmax = this.config.maxSize || 10000;
44768          var center = this.mgr.getRegion("center");
44769          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44770     },
44771
44772     onSplitMove : function(split, newSize){
44773         this.fireEvent("resized", this, newSize);
44774     },
44775     
44776     /** 
44777      * Returns the {@link Roo.SplitBar} for this region.
44778      * @return {Roo.SplitBar}
44779      */
44780     getSplitBar : function(){
44781         return this.split;
44782     },
44783     
44784     hide : function(){
44785         this.hideSplitter();
44786         Roo.SplitLayoutRegion.superclass.hide.call(this);
44787     },
44788
44789     hideSplitter : function(){
44790         if(this.split){
44791             this.split.el.setLocation(-2000,-2000);
44792             this.split.el.hide();
44793         }
44794     },
44795
44796     show : function(){
44797         if(this.split){
44798             this.split.el.show();
44799         }
44800         Roo.SplitLayoutRegion.superclass.show.call(this);
44801     },
44802     
44803     beforeSlide: function(){
44804         if(Roo.isGecko){// firefox overflow auto bug workaround
44805             this.bodyEl.clip();
44806             if(this.tabs) this.tabs.bodyEl.clip();
44807             if(this.activePanel){
44808                 this.activePanel.getEl().clip();
44809                 
44810                 if(this.activePanel.beforeSlide){
44811                     this.activePanel.beforeSlide();
44812                 }
44813             }
44814         }
44815     },
44816     
44817     afterSlide : function(){
44818         if(Roo.isGecko){// firefox overflow auto bug workaround
44819             this.bodyEl.unclip();
44820             if(this.tabs) this.tabs.bodyEl.unclip();
44821             if(this.activePanel){
44822                 this.activePanel.getEl().unclip();
44823                 if(this.activePanel.afterSlide){
44824                     this.activePanel.afterSlide();
44825                 }
44826             }
44827         }
44828     },
44829
44830     initAutoHide : function(){
44831         if(this.autoHide !== false){
44832             if(!this.autoHideHd){
44833                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44834                 this.autoHideHd = {
44835                     "mouseout": function(e){
44836                         if(!e.within(this.el, true)){
44837                             st.delay(500);
44838                         }
44839                     },
44840                     "mouseover" : function(e){
44841                         st.cancel();
44842                     },
44843                     scope : this
44844                 };
44845             }
44846             this.el.on(this.autoHideHd);
44847         }
44848     },
44849
44850     clearAutoHide : function(){
44851         if(this.autoHide !== false){
44852             this.el.un("mouseout", this.autoHideHd.mouseout);
44853             this.el.un("mouseover", this.autoHideHd.mouseover);
44854         }
44855     },
44856
44857     clearMonitor : function(){
44858         Roo.get(document).un("click", this.slideInIf, this);
44859     },
44860
44861     // these names are backwards but not changed for compat
44862     slideOut : function(){
44863         if(this.isSlid || this.el.hasActiveFx()){
44864             return;
44865         }
44866         this.isSlid = true;
44867         if(this.collapseBtn){
44868             this.collapseBtn.hide();
44869         }
44870         this.closeBtnState = this.closeBtn.getStyle('display');
44871         this.closeBtn.hide();
44872         if(this.stickBtn){
44873             this.stickBtn.show();
44874         }
44875         this.el.show();
44876         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44877         this.beforeSlide();
44878         this.el.setStyle("z-index", 10001);
44879         this.el.slideIn(this.getSlideAnchor(), {
44880             callback: function(){
44881                 this.afterSlide();
44882                 this.initAutoHide();
44883                 Roo.get(document).on("click", this.slideInIf, this);
44884                 this.fireEvent("slideshow", this);
44885             },
44886             scope: this,
44887             block: true
44888         });
44889     },
44890
44891     afterSlideIn : function(){
44892         this.clearAutoHide();
44893         this.isSlid = false;
44894         this.clearMonitor();
44895         this.el.setStyle("z-index", "");
44896         if(this.collapseBtn){
44897             this.collapseBtn.show();
44898         }
44899         this.closeBtn.setStyle('display', this.closeBtnState);
44900         if(this.stickBtn){
44901             this.stickBtn.hide();
44902         }
44903         this.fireEvent("slidehide", this);
44904     },
44905
44906     slideIn : function(cb){
44907         if(!this.isSlid || this.el.hasActiveFx()){
44908             Roo.callback(cb);
44909             return;
44910         }
44911         this.isSlid = false;
44912         this.beforeSlide();
44913         this.el.slideOut(this.getSlideAnchor(), {
44914             callback: function(){
44915                 this.el.setLeftTop(-10000, -10000);
44916                 this.afterSlide();
44917                 this.afterSlideIn();
44918                 Roo.callback(cb);
44919             },
44920             scope: this,
44921             block: true
44922         });
44923     },
44924     
44925     slideInIf : function(e){
44926         if(!e.within(this.el)){
44927             this.slideIn();
44928         }
44929     },
44930
44931     animateCollapse : function(){
44932         this.beforeSlide();
44933         this.el.setStyle("z-index", 20000);
44934         var anchor = this.getSlideAnchor();
44935         this.el.slideOut(anchor, {
44936             callback : function(){
44937                 this.el.setStyle("z-index", "");
44938                 this.collapsedEl.slideIn(anchor, {duration:.3});
44939                 this.afterSlide();
44940                 this.el.setLocation(-10000,-10000);
44941                 this.el.hide();
44942                 this.fireEvent("collapsed", this);
44943             },
44944             scope: this,
44945             block: true
44946         });
44947     },
44948
44949     animateExpand : function(){
44950         this.beforeSlide();
44951         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44952         this.el.setStyle("z-index", 20000);
44953         this.collapsedEl.hide({
44954             duration:.1
44955         });
44956         this.el.slideIn(this.getSlideAnchor(), {
44957             callback : function(){
44958                 this.el.setStyle("z-index", "");
44959                 this.afterSlide();
44960                 if(this.split){
44961                     this.split.el.show();
44962                 }
44963                 this.fireEvent("invalidated", this);
44964                 this.fireEvent("expanded", this);
44965             },
44966             scope: this,
44967             block: true
44968         });
44969     },
44970
44971     anchors : {
44972         "west" : "left",
44973         "east" : "right",
44974         "north" : "top",
44975         "south" : "bottom"
44976     },
44977
44978     sanchors : {
44979         "west" : "l",
44980         "east" : "r",
44981         "north" : "t",
44982         "south" : "b"
44983     },
44984
44985     canchors : {
44986         "west" : "tl-tr",
44987         "east" : "tr-tl",
44988         "north" : "tl-bl",
44989         "south" : "bl-tl"
44990     },
44991
44992     getAnchor : function(){
44993         return this.anchors[this.position];
44994     },
44995
44996     getCollapseAnchor : function(){
44997         return this.canchors[this.position];
44998     },
44999
45000     getSlideAnchor : function(){
45001         return this.sanchors[this.position];
45002     },
45003
45004     getAlignAdj : function(){
45005         var cm = this.cmargins;
45006         switch(this.position){
45007             case "west":
45008                 return [0, 0];
45009             break;
45010             case "east":
45011                 return [0, 0];
45012             break;
45013             case "north":
45014                 return [0, 0];
45015             break;
45016             case "south":
45017                 return [0, 0];
45018             break;
45019         }
45020     },
45021
45022     getExpandAdj : function(){
45023         var c = this.collapsedEl, cm = this.cmargins;
45024         switch(this.position){
45025             case "west":
45026                 return [-(cm.right+c.getWidth()+cm.left), 0];
45027             break;
45028             case "east":
45029                 return [cm.right+c.getWidth()+cm.left, 0];
45030             break;
45031             case "north":
45032                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45033             break;
45034             case "south":
45035                 return [0, cm.top+cm.bottom+c.getHeight()];
45036             break;
45037         }
45038     }
45039 });/*
45040  * Based on:
45041  * Ext JS Library 1.1.1
45042  * Copyright(c) 2006-2007, Ext JS, LLC.
45043  *
45044  * Originally Released Under LGPL - original licence link has changed is not relivant.
45045  *
45046  * Fork - LGPL
45047  * <script type="text/javascript">
45048  */
45049 /*
45050  * These classes are private internal classes
45051  */
45052 Roo.CenterLayoutRegion = function(mgr, config){
45053     Roo.LayoutRegion.call(this, mgr, config, "center");
45054     this.visible = true;
45055     this.minWidth = config.minWidth || 20;
45056     this.minHeight = config.minHeight || 20;
45057 };
45058
45059 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45060     hide : function(){
45061         // center panel can't be hidden
45062     },
45063     
45064     show : function(){
45065         // center panel can't be hidden
45066     },
45067     
45068     getMinWidth: function(){
45069         return this.minWidth;
45070     },
45071     
45072     getMinHeight: function(){
45073         return this.minHeight;
45074     }
45075 });
45076
45077
45078 Roo.NorthLayoutRegion = function(mgr, config){
45079     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45080     if(this.split){
45081         this.split.placement = Roo.SplitBar.TOP;
45082         this.split.orientation = Roo.SplitBar.VERTICAL;
45083         this.split.el.addClass("x-layout-split-v");
45084     }
45085     var size = config.initialSize || config.height;
45086     if(typeof size != "undefined"){
45087         this.el.setHeight(size);
45088     }
45089 };
45090 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45091     orientation: Roo.SplitBar.VERTICAL,
45092     getBox : function(){
45093         if(this.collapsed){
45094             return this.collapsedEl.getBox();
45095         }
45096         var box = this.el.getBox();
45097         if(this.split){
45098             box.height += this.split.el.getHeight();
45099         }
45100         return box;
45101     },
45102     
45103     updateBox : function(box){
45104         if(this.split && !this.collapsed){
45105             box.height -= this.split.el.getHeight();
45106             this.split.el.setLeft(box.x);
45107             this.split.el.setTop(box.y+box.height);
45108             this.split.el.setWidth(box.width);
45109         }
45110         if(this.collapsed){
45111             this.updateBody(box.width, null);
45112         }
45113         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45114     }
45115 });
45116
45117 Roo.SouthLayoutRegion = function(mgr, config){
45118     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45119     if(this.split){
45120         this.split.placement = Roo.SplitBar.BOTTOM;
45121         this.split.orientation = Roo.SplitBar.VERTICAL;
45122         this.split.el.addClass("x-layout-split-v");
45123     }
45124     var size = config.initialSize || config.height;
45125     if(typeof size != "undefined"){
45126         this.el.setHeight(size);
45127     }
45128 };
45129 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45130     orientation: Roo.SplitBar.VERTICAL,
45131     getBox : function(){
45132         if(this.collapsed){
45133             return this.collapsedEl.getBox();
45134         }
45135         var box = this.el.getBox();
45136         if(this.split){
45137             var sh = this.split.el.getHeight();
45138             box.height += sh;
45139             box.y -= sh;
45140         }
45141         return box;
45142     },
45143     
45144     updateBox : function(box){
45145         if(this.split && !this.collapsed){
45146             var sh = this.split.el.getHeight();
45147             box.height -= sh;
45148             box.y += sh;
45149             this.split.el.setLeft(box.x);
45150             this.split.el.setTop(box.y-sh);
45151             this.split.el.setWidth(box.width);
45152         }
45153         if(this.collapsed){
45154             this.updateBody(box.width, null);
45155         }
45156         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45157     }
45158 });
45159
45160 Roo.EastLayoutRegion = function(mgr, config){
45161     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45162     if(this.split){
45163         this.split.placement = Roo.SplitBar.RIGHT;
45164         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45165         this.split.el.addClass("x-layout-split-h");
45166     }
45167     var size = config.initialSize || config.width;
45168     if(typeof size != "undefined"){
45169         this.el.setWidth(size);
45170     }
45171 };
45172 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45173     orientation: Roo.SplitBar.HORIZONTAL,
45174     getBox : function(){
45175         if(this.collapsed){
45176             return this.collapsedEl.getBox();
45177         }
45178         var box = this.el.getBox();
45179         if(this.split){
45180             var sw = this.split.el.getWidth();
45181             box.width += sw;
45182             box.x -= sw;
45183         }
45184         return box;
45185     },
45186
45187     updateBox : function(box){
45188         if(this.split && !this.collapsed){
45189             var sw = this.split.el.getWidth();
45190             box.width -= sw;
45191             this.split.el.setLeft(box.x);
45192             this.split.el.setTop(box.y);
45193             this.split.el.setHeight(box.height);
45194             box.x += sw;
45195         }
45196         if(this.collapsed){
45197             this.updateBody(null, box.height);
45198         }
45199         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45200     }
45201 });
45202
45203 Roo.WestLayoutRegion = function(mgr, config){
45204     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45205     if(this.split){
45206         this.split.placement = Roo.SplitBar.LEFT;
45207         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45208         this.split.el.addClass("x-layout-split-h");
45209     }
45210     var size = config.initialSize || config.width;
45211     if(typeof size != "undefined"){
45212         this.el.setWidth(size);
45213     }
45214 };
45215 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45216     orientation: Roo.SplitBar.HORIZONTAL,
45217     getBox : function(){
45218         if(this.collapsed){
45219             return this.collapsedEl.getBox();
45220         }
45221         var box = this.el.getBox();
45222         if(this.split){
45223             box.width += this.split.el.getWidth();
45224         }
45225         return box;
45226     },
45227     
45228     updateBox : function(box){
45229         if(this.split && !this.collapsed){
45230             var sw = this.split.el.getWidth();
45231             box.width -= sw;
45232             this.split.el.setLeft(box.x+box.width);
45233             this.split.el.setTop(box.y);
45234             this.split.el.setHeight(box.height);
45235         }
45236         if(this.collapsed){
45237             this.updateBody(null, box.height);
45238         }
45239         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45240     }
45241 });
45242 /*
45243  * Based on:
45244  * Ext JS Library 1.1.1
45245  * Copyright(c) 2006-2007, Ext JS, LLC.
45246  *
45247  * Originally Released Under LGPL - original licence link has changed is not relivant.
45248  *
45249  * Fork - LGPL
45250  * <script type="text/javascript">
45251  */
45252  
45253  
45254 /*
45255  * Private internal class for reading and applying state
45256  */
45257 Roo.LayoutStateManager = function(layout){
45258      // default empty state
45259      this.state = {
45260         north: {},
45261         south: {},
45262         east: {},
45263         west: {}       
45264     };
45265 };
45266
45267 Roo.LayoutStateManager.prototype = {
45268     init : function(layout, provider){
45269         this.provider = provider;
45270         var state = provider.get(layout.id+"-layout-state");
45271         if(state){
45272             var wasUpdating = layout.isUpdating();
45273             if(!wasUpdating){
45274                 layout.beginUpdate();
45275             }
45276             for(var key in state){
45277                 if(typeof state[key] != "function"){
45278                     var rstate = state[key];
45279                     var r = layout.getRegion(key);
45280                     if(r && rstate){
45281                         if(rstate.size){
45282                             r.resizeTo(rstate.size);
45283                         }
45284                         if(rstate.collapsed == true){
45285                             r.collapse(true);
45286                         }else{
45287                             r.expand(null, true);
45288                         }
45289                     }
45290                 }
45291             }
45292             if(!wasUpdating){
45293                 layout.endUpdate();
45294             }
45295             this.state = state; 
45296         }
45297         this.layout = layout;
45298         layout.on("regionresized", this.onRegionResized, this);
45299         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45300         layout.on("regionexpanded", this.onRegionExpanded, this);
45301     },
45302     
45303     storeState : function(){
45304         this.provider.set(this.layout.id+"-layout-state", this.state);
45305     },
45306     
45307     onRegionResized : function(region, newSize){
45308         this.state[region.getPosition()].size = newSize;
45309         this.storeState();
45310     },
45311     
45312     onRegionCollapsed : function(region){
45313         this.state[region.getPosition()].collapsed = true;
45314         this.storeState();
45315     },
45316     
45317     onRegionExpanded : function(region){
45318         this.state[region.getPosition()].collapsed = false;
45319         this.storeState();
45320     }
45321 };/*
45322  * Based on:
45323  * Ext JS Library 1.1.1
45324  * Copyright(c) 2006-2007, Ext JS, LLC.
45325  *
45326  * Originally Released Under LGPL - original licence link has changed is not relivant.
45327  *
45328  * Fork - LGPL
45329  * <script type="text/javascript">
45330  */
45331 /**
45332  * @class Roo.ContentPanel
45333  * @extends Roo.util.Observable
45334  * A basic ContentPanel element.
45335  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45336  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45337  * @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
45338  * @cfg {Boolean} closable True if the panel can be closed/removed
45339  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45340  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45341  * @cfg {Toolbar} toolbar A toolbar for this panel
45342  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45343  * @cfg {String} title The title for this panel
45344  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45345  * @cfg {String} url Calls {@link #setUrl} with this value
45346  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45347  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45348  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45349  * @constructor
45350  * Create a new ContentPanel.
45351  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45352  * @param {String/Object} config A string to set only the title or a config object
45353  * @param {String} content (optional) Set the HTML content for this panel
45354  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45355  */
45356 Roo.ContentPanel = function(el, config, content){
45357     
45358      
45359     /*
45360     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45361         config = el;
45362         el = Roo.id();
45363     }
45364     if (config && config.parentLayout) { 
45365         el = config.parentLayout.el.createChild(); 
45366     }
45367     */
45368     if(el.autoCreate){ // xtype is available if this is called from factory
45369         config = el;
45370         el = Roo.id();
45371     }
45372     this.el = Roo.get(el);
45373     if(!this.el && config && config.autoCreate){
45374         if(typeof config.autoCreate == "object"){
45375             if(!config.autoCreate.id){
45376                 config.autoCreate.id = config.id||el;
45377             }
45378             this.el = Roo.DomHelper.append(document.body,
45379                         config.autoCreate, true);
45380         }else{
45381             this.el = Roo.DomHelper.append(document.body,
45382                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45383         }
45384     }
45385     this.closable = false;
45386     this.loaded = false;
45387     this.active = false;
45388     if(typeof config == "string"){
45389         this.title = config;
45390     }else{
45391         Roo.apply(this, config);
45392     }
45393     
45394     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45395         this.wrapEl = this.el.wrap();    
45396         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45397         
45398     }
45399     
45400     
45401     
45402     if(this.resizeEl){
45403         this.resizeEl = Roo.get(this.resizeEl, true);
45404     }else{
45405         this.resizeEl = this.el;
45406     }
45407     this.addEvents({
45408         /**
45409          * @event activate
45410          * Fires when this panel is activated. 
45411          * @param {Roo.ContentPanel} this
45412          */
45413         "activate" : true,
45414         /**
45415          * @event deactivate
45416          * Fires when this panel is activated. 
45417          * @param {Roo.ContentPanel} this
45418          */
45419         "deactivate" : true,
45420
45421         /**
45422          * @event resize
45423          * Fires when this panel is resized if fitToFrame is true.
45424          * @param {Roo.ContentPanel} this
45425          * @param {Number} width The width after any component adjustments
45426          * @param {Number} height The height after any component adjustments
45427          */
45428         "resize" : true
45429     });
45430     if(this.autoScroll){
45431         this.resizeEl.setStyle("overflow", "auto");
45432     } else {
45433         // fix randome scrolling
45434         this.el.on('scroll', function() {
45435             this.scrollTo('top',0); 
45436         });
45437     }
45438     content = content || this.content;
45439     if(content){
45440         this.setContent(content);
45441     }
45442     if(config && config.url){
45443         this.setUrl(this.url, this.params, this.loadOnce);
45444     }
45445     
45446     
45447     
45448     Roo.ContentPanel.superclass.constructor.call(this);
45449 };
45450
45451 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45452     tabTip:'',
45453     setRegion : function(region){
45454         this.region = region;
45455         if(region){
45456            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45457         }else{
45458            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45459         } 
45460     },
45461     
45462     /**
45463      * Returns the toolbar for this Panel if one was configured. 
45464      * @return {Roo.Toolbar} 
45465      */
45466     getToolbar : function(){
45467         return this.toolbar;
45468     },
45469     
45470     setActiveState : function(active){
45471         this.active = active;
45472         if(!active){
45473             this.fireEvent("deactivate", this);
45474         }else{
45475             this.fireEvent("activate", this);
45476         }
45477     },
45478     /**
45479      * Updates this panel's element
45480      * @param {String} content The new content
45481      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45482     */
45483     setContent : function(content, loadScripts){
45484         this.el.update(content, loadScripts);
45485     },
45486
45487     ignoreResize : function(w, h){
45488         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45489             return true;
45490         }else{
45491             this.lastSize = {width: w, height: h};
45492             return false;
45493         }
45494     },
45495     /**
45496      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45497      * @return {Roo.UpdateManager} The UpdateManager
45498      */
45499     getUpdateManager : function(){
45500         return this.el.getUpdateManager();
45501     },
45502      /**
45503      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45504      * @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:
45505 <pre><code>
45506 panel.load({
45507     url: "your-url.php",
45508     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45509     callback: yourFunction,
45510     scope: yourObject, //(optional scope)
45511     discardUrl: false,
45512     nocache: false,
45513     text: "Loading...",
45514     timeout: 30,
45515     scripts: false
45516 });
45517 </code></pre>
45518      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45519      * 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.
45520      * @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}
45521      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45522      * @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.
45523      * @return {Roo.ContentPanel} this
45524      */
45525     load : function(){
45526         var um = this.el.getUpdateManager();
45527         um.update.apply(um, arguments);
45528         return this;
45529     },
45530
45531
45532     /**
45533      * 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.
45534      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45535      * @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)
45536      * @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)
45537      * @return {Roo.UpdateManager} The UpdateManager
45538      */
45539     setUrl : function(url, params, loadOnce){
45540         if(this.refreshDelegate){
45541             this.removeListener("activate", this.refreshDelegate);
45542         }
45543         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45544         this.on("activate", this.refreshDelegate);
45545         return this.el.getUpdateManager();
45546     },
45547     
45548     _handleRefresh : function(url, params, loadOnce){
45549         if(!loadOnce || !this.loaded){
45550             var updater = this.el.getUpdateManager();
45551             updater.update(url, params, this._setLoaded.createDelegate(this));
45552         }
45553     },
45554     
45555     _setLoaded : function(){
45556         this.loaded = true;
45557     }, 
45558     
45559     /**
45560      * Returns this panel's id
45561      * @return {String} 
45562      */
45563     getId : function(){
45564         return this.el.id;
45565     },
45566     
45567     /** 
45568      * Returns this panel's element - used by regiosn to add.
45569      * @return {Roo.Element} 
45570      */
45571     getEl : function(){
45572         return this.wrapEl || this.el;
45573     },
45574     
45575     adjustForComponents : function(width, height){
45576         if(this.resizeEl != this.el){
45577             width -= this.el.getFrameWidth('lr');
45578             height -= this.el.getFrameWidth('tb');
45579         }
45580         if(this.toolbar){
45581             var te = this.toolbar.getEl();
45582             height -= te.getHeight();
45583             te.setWidth(width);
45584         }
45585         if(this.adjustments){
45586             width += this.adjustments[0];
45587             height += this.adjustments[1];
45588         }
45589         return {"width": width, "height": height};
45590     },
45591     
45592     setSize : function(width, height){
45593         if(this.fitToFrame && !this.ignoreResize(width, height)){
45594             if(this.fitContainer && this.resizeEl != this.el){
45595                 this.el.setSize(width, height);
45596             }
45597             var size = this.adjustForComponents(width, height);
45598             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45599             this.fireEvent('resize', this, size.width, size.height);
45600         }
45601     },
45602     
45603     /**
45604      * Returns this panel's title
45605      * @return {String} 
45606      */
45607     getTitle : function(){
45608         return this.title;
45609     },
45610     
45611     /**
45612      * Set this panel's title
45613      * @param {String} title
45614      */
45615     setTitle : function(title){
45616         this.title = title;
45617         if(this.region){
45618             this.region.updatePanelTitle(this, title);
45619         }
45620     },
45621     
45622     /**
45623      * Returns true is this panel was configured to be closable
45624      * @return {Boolean} 
45625      */
45626     isClosable : function(){
45627         return this.closable;
45628     },
45629     
45630     beforeSlide : function(){
45631         this.el.clip();
45632         this.resizeEl.clip();
45633     },
45634     
45635     afterSlide : function(){
45636         this.el.unclip();
45637         this.resizeEl.unclip();
45638     },
45639     
45640     /**
45641      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45642      *   Will fail silently if the {@link #setUrl} method has not been called.
45643      *   This does not activate the panel, just updates its content.
45644      */
45645     refresh : function(){
45646         if(this.refreshDelegate){
45647            this.loaded = false;
45648            this.refreshDelegate();
45649         }
45650     },
45651     
45652     /**
45653      * Destroys this panel
45654      */
45655     destroy : function(){
45656         this.el.removeAllListeners();
45657         var tempEl = document.createElement("span");
45658         tempEl.appendChild(this.el.dom);
45659         tempEl.innerHTML = "";
45660         this.el.remove();
45661         this.el = null;
45662     },
45663     
45664       /**
45665      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45666      * <pre><code>
45667
45668 layout.addxtype({
45669        xtype : 'Form',
45670        items: [ .... ]
45671    }
45672 );
45673
45674 </code></pre>
45675      * @param {Object} cfg Xtype definition of item to add.
45676      */
45677     
45678     addxtype : function(cfg) {
45679         // add form..
45680         if (cfg.xtype.match(/^Form$/)) {
45681             var el = this.el.createChild();
45682
45683             this.form = new  Roo.form.Form(cfg);
45684             
45685             
45686             if ( this.form.allItems.length) this.form.render(el.dom);
45687             return this.form;
45688         }
45689         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45690             // views..
45691             cfg.el = this.el.appendChild(document.createElement("div"));
45692             // factory?
45693             var ret = new Roo[cfg.xtype](cfg);
45694             ret.render(false, ''); // render blank..
45695             return ret;
45696             
45697         }
45698         return false;
45699         
45700     }
45701 });
45702
45703 /**
45704  * @class Roo.GridPanel
45705  * @extends Roo.ContentPanel
45706  * @constructor
45707  * Create a new GridPanel.
45708  * @param {Roo.grid.Grid} grid The grid for this panel
45709  * @param {String/Object} config A string to set only the panel's title, or a config object
45710  */
45711 Roo.GridPanel = function(grid, config){
45712     
45713   
45714     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45715         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45716         
45717     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45718     
45719     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45720     
45721     if(this.toolbar){
45722         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45723     }
45724     // xtype created footer. - not sure if will work as we normally have to render first..
45725     if (this.footer && !this.footer.el && this.footer.xtype) {
45726         
45727         this.footer.container = this.grid.getView().getFooterPanel(true);
45728         this.footer.dataSource = this.grid.dataSource;
45729         this.footer = Roo.factory(this.footer, Roo);
45730         
45731     }
45732     
45733     grid.monitorWindowResize = false; // turn off autosizing
45734     grid.autoHeight = false;
45735     grid.autoWidth = false;
45736     this.grid = grid;
45737     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45738 };
45739
45740 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45741     getId : function(){
45742         return this.grid.id;
45743     },
45744     
45745     /**
45746      * Returns the grid for this panel
45747      * @return {Roo.grid.Grid} 
45748      */
45749     getGrid : function(){
45750         return this.grid;    
45751     },
45752     
45753     setSize : function(width, height){
45754         if(!this.ignoreResize(width, height)){
45755             var grid = this.grid;
45756             var size = this.adjustForComponents(width, height);
45757             grid.getGridEl().setSize(size.width, size.height);
45758             grid.autoSize();
45759         }
45760     },
45761     
45762     beforeSlide : function(){
45763         this.grid.getView().scroller.clip();
45764     },
45765     
45766     afterSlide : function(){
45767         this.grid.getView().scroller.unclip();
45768     },
45769     
45770     destroy : function(){
45771         this.grid.destroy();
45772         delete this.grid;
45773         Roo.GridPanel.superclass.destroy.call(this); 
45774     }
45775 });
45776
45777
45778 /**
45779  * @class Roo.NestedLayoutPanel
45780  * @extends Roo.ContentPanel
45781  * @constructor
45782  * Create a new NestedLayoutPanel.
45783  * 
45784  * 
45785  * @param {Roo.BorderLayout} layout The layout for this panel
45786  * @param {String/Object} config A string to set only the title or a config object
45787  */
45788 Roo.NestedLayoutPanel = function(layout, config)
45789 {
45790     // construct with only one argument..
45791     /* FIXME - implement nicer consturctors
45792     if (layout.layout) {
45793         config = layout;
45794         layout = config.layout;
45795         delete config.layout;
45796     }
45797     if (layout.xtype && !layout.getEl) {
45798         // then layout needs constructing..
45799         layout = Roo.factory(layout, Roo);
45800     }
45801     */
45802     
45803     
45804     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45805     
45806     layout.monitorWindowResize = false; // turn off autosizing
45807     this.layout = layout;
45808     this.layout.getEl().addClass("x-layout-nested-layout");
45809     
45810     
45811     
45812     
45813 };
45814
45815 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45816
45817     setSize : function(width, height){
45818         if(!this.ignoreResize(width, height)){
45819             var size = this.adjustForComponents(width, height);
45820             var el = this.layout.getEl();
45821             el.setSize(size.width, size.height);
45822             var touch = el.dom.offsetWidth;
45823             this.layout.layout();
45824             // ie requires a double layout on the first pass
45825             if(Roo.isIE && !this.initialized){
45826                 this.initialized = true;
45827                 this.layout.layout();
45828             }
45829         }
45830     },
45831     
45832     // activate all subpanels if not currently active..
45833     
45834     setActiveState : function(active){
45835         this.active = active;
45836         if(!active){
45837             this.fireEvent("deactivate", this);
45838             return;
45839         }
45840         
45841         this.fireEvent("activate", this);
45842         // not sure if this should happen before or after..
45843         if (!this.layout) {
45844             return; // should not happen..
45845         }
45846         var reg = false;
45847         for (var r in this.layout.regions) {
45848             reg = this.layout.getRegion(r);
45849             if (reg.getActivePanel()) {
45850                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45851                 reg.setActivePanel(reg.getActivePanel());
45852                 continue;
45853             }
45854             if (!reg.panels.length) {
45855                 continue;
45856             }
45857             reg.showPanel(reg.getPanel(0));
45858         }
45859         
45860         
45861         
45862         
45863     },
45864     
45865     /**
45866      * Returns the nested BorderLayout for this panel
45867      * @return {Roo.BorderLayout} 
45868      */
45869     getLayout : function(){
45870         return this.layout;
45871     },
45872     
45873      /**
45874      * Adds a xtype elements to the layout of the nested panel
45875      * <pre><code>
45876
45877 panel.addxtype({
45878        xtype : 'ContentPanel',
45879        region: 'west',
45880        items: [ .... ]
45881    }
45882 );
45883
45884 panel.addxtype({
45885         xtype : 'NestedLayoutPanel',
45886         region: 'west',
45887         layout: {
45888            center: { },
45889            west: { }   
45890         },
45891         items : [ ... list of content panels or nested layout panels.. ]
45892    }
45893 );
45894 </code></pre>
45895      * @param {Object} cfg Xtype definition of item to add.
45896      */
45897     addxtype : function(cfg) {
45898         return this.layout.addxtype(cfg);
45899     
45900     }
45901 });
45902
45903 Roo.ScrollPanel = function(el, config, content){
45904     config = config || {};
45905     config.fitToFrame = true;
45906     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45907     
45908     this.el.dom.style.overflow = "hidden";
45909     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45910     this.el.removeClass("x-layout-inactive-content");
45911     this.el.on("mousewheel", this.onWheel, this);
45912
45913     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45914     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45915     up.unselectable(); down.unselectable();
45916     up.on("click", this.scrollUp, this);
45917     down.on("click", this.scrollDown, this);
45918     up.addClassOnOver("x-scroller-btn-over");
45919     down.addClassOnOver("x-scroller-btn-over");
45920     up.addClassOnClick("x-scroller-btn-click");
45921     down.addClassOnClick("x-scroller-btn-click");
45922     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45923
45924     this.resizeEl = this.el;
45925     this.el = wrap; this.up = up; this.down = down;
45926 };
45927
45928 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45929     increment : 100,
45930     wheelIncrement : 5,
45931     scrollUp : function(){
45932         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45933     },
45934
45935     scrollDown : function(){
45936         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45937     },
45938
45939     afterScroll : function(){
45940         var el = this.resizeEl;
45941         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45942         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45943         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45944     },
45945
45946     setSize : function(){
45947         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45948         this.afterScroll();
45949     },
45950
45951     onWheel : function(e){
45952         var d = e.getWheelDelta();
45953         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45954         this.afterScroll();
45955         e.stopEvent();
45956     },
45957
45958     setContent : function(content, loadScripts){
45959         this.resizeEl.update(content, loadScripts);
45960     }
45961
45962 });
45963
45964
45965
45966
45967
45968
45969
45970
45971
45972 /**
45973  * @class Roo.TreePanel
45974  * @extends Roo.ContentPanel
45975  * @constructor
45976  * Create a new TreePanel. - defaults to fit/scoll contents.
45977  * @param {String/Object} config A string to set only the panel's title, or a config object
45978  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45979  */
45980 Roo.TreePanel = function(config){
45981     var el = config.el;
45982     var tree = config.tree;
45983     delete config.tree; 
45984     delete config.el; // hopefull!
45985     
45986     // wrapper for IE7 strict & safari scroll issue
45987     
45988     var treeEl = el.createChild();
45989     config.resizeEl = treeEl;
45990     
45991     
45992     
45993     Roo.TreePanel.superclass.constructor.call(this, el, config);
45994  
45995  
45996     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45997     //console.log(tree);
45998     this.on('activate', function()
45999     {
46000         if (this.tree.rendered) {
46001             return;
46002         }
46003         //console.log('render tree');
46004         this.tree.render();
46005     });
46006     
46007     this.on('resize',  function (cp, w, h) {
46008             this.tree.innerCt.setWidth(w);
46009             this.tree.innerCt.setHeight(h);
46010             this.tree.innerCt.setStyle('overflow-y', 'auto');
46011     });
46012
46013         
46014     
46015 };
46016
46017 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46018     fitToFrame : true,
46019     autoScroll : true
46020 });
46021
46022
46023
46024
46025
46026
46027
46028
46029
46030
46031
46032 /*
46033  * Based on:
46034  * Ext JS Library 1.1.1
46035  * Copyright(c) 2006-2007, Ext JS, LLC.
46036  *
46037  * Originally Released Under LGPL - original licence link has changed is not relivant.
46038  *
46039  * Fork - LGPL
46040  * <script type="text/javascript">
46041  */
46042  
46043
46044 /**
46045  * @class Roo.ReaderLayout
46046  * @extends Roo.BorderLayout
46047  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46048  * center region containing two nested regions (a top one for a list view and one for item preview below),
46049  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46050  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46051  * expedites the setup of the overall layout and regions for this common application style.
46052  * Example:
46053  <pre><code>
46054 var reader = new Roo.ReaderLayout();
46055 var CP = Roo.ContentPanel;  // shortcut for adding
46056
46057 reader.beginUpdate();
46058 reader.add("north", new CP("north", "North"));
46059 reader.add("west", new CP("west", {title: "West"}));
46060 reader.add("east", new CP("east", {title: "East"}));
46061
46062 reader.regions.listView.add(new CP("listView", "List"));
46063 reader.regions.preview.add(new CP("preview", "Preview"));
46064 reader.endUpdate();
46065 </code></pre>
46066 * @constructor
46067 * Create a new ReaderLayout
46068 * @param {Object} config Configuration options
46069 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46070 * document.body if omitted)
46071 */
46072 Roo.ReaderLayout = function(config, renderTo){
46073     var c = config || {size:{}};
46074     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46075         north: c.north !== false ? Roo.apply({
46076             split:false,
46077             initialSize: 32,
46078             titlebar: false
46079         }, c.north) : false,
46080         west: c.west !== false ? Roo.apply({
46081             split:true,
46082             initialSize: 200,
46083             minSize: 175,
46084             maxSize: 400,
46085             titlebar: true,
46086             collapsible: true,
46087             animate: true,
46088             margins:{left:5,right:0,bottom:5,top:5},
46089             cmargins:{left:5,right:5,bottom:5,top:5}
46090         }, c.west) : false,
46091         east: c.east !== false ? Roo.apply({
46092             split:true,
46093             initialSize: 200,
46094             minSize: 175,
46095             maxSize: 400,
46096             titlebar: true,
46097             collapsible: true,
46098             animate: true,
46099             margins:{left:0,right:5,bottom:5,top:5},
46100             cmargins:{left:5,right:5,bottom:5,top:5}
46101         }, c.east) : false,
46102         center: Roo.apply({
46103             tabPosition: 'top',
46104             autoScroll:false,
46105             closeOnTab: true,
46106             titlebar:false,
46107             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46108         }, c.center)
46109     });
46110
46111     this.el.addClass('x-reader');
46112
46113     this.beginUpdate();
46114
46115     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46116         south: c.preview !== false ? Roo.apply({
46117             split:true,
46118             initialSize: 200,
46119             minSize: 100,
46120             autoScroll:true,
46121             collapsible:true,
46122             titlebar: true,
46123             cmargins:{top:5,left:0, right:0, bottom:0}
46124         }, c.preview) : false,
46125         center: Roo.apply({
46126             autoScroll:false,
46127             titlebar:false,
46128             minHeight:200
46129         }, c.listView)
46130     });
46131     this.add('center', new Roo.NestedLayoutPanel(inner,
46132             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46133
46134     this.endUpdate();
46135
46136     this.regions.preview = inner.getRegion('south');
46137     this.regions.listView = inner.getRegion('center');
46138 };
46139
46140 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46141  * Based on:
46142  * Ext JS Library 1.1.1
46143  * Copyright(c) 2006-2007, Ext JS, LLC.
46144  *
46145  * Originally Released Under LGPL - original licence link has changed is not relivant.
46146  *
46147  * Fork - LGPL
46148  * <script type="text/javascript">
46149  */
46150  
46151 /**
46152  * @class Roo.grid.Grid
46153  * @extends Roo.util.Observable
46154  * This class represents the primary interface of a component based grid control.
46155  * <br><br>Usage:<pre><code>
46156  var grid = new Roo.grid.Grid("my-container-id", {
46157      ds: myDataStore,
46158      cm: myColModel,
46159      selModel: mySelectionModel,
46160      autoSizeColumns: true,
46161      monitorWindowResize: false,
46162      trackMouseOver: true
46163  });
46164  // set any options
46165  grid.render();
46166  * </code></pre>
46167  * <b>Common Problems:</b><br/>
46168  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46169  * element will correct this<br/>
46170  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46171  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46172  * are unpredictable.<br/>
46173  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46174  * grid to calculate dimensions/offsets.<br/>
46175   * @constructor
46176  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46177  * The container MUST have some type of size defined for the grid to fill. The container will be
46178  * automatically set to position relative if it isn't already.
46179  * @param {Object} config A config object that sets properties on this grid.
46180  */
46181 Roo.grid.Grid = function(container, config){
46182         // initialize the container
46183         this.container = Roo.get(container);
46184         this.container.update("");
46185         this.container.setStyle("overflow", "hidden");
46186     this.container.addClass('x-grid-container');
46187
46188     this.id = this.container.id;
46189
46190     Roo.apply(this, config);
46191     // check and correct shorthanded configs
46192     if(this.ds){
46193         this.dataSource = this.ds;
46194         delete this.ds;
46195     }
46196     if(this.cm){
46197         this.colModel = this.cm;
46198         delete this.cm;
46199     }
46200     if(this.sm){
46201         this.selModel = this.sm;
46202         delete this.sm;
46203     }
46204
46205     if (this.selModel) {
46206         this.selModel = Roo.factory(this.selModel, Roo.grid);
46207         this.sm = this.selModel;
46208         this.sm.xmodule = this.xmodule || false;
46209     }
46210     if (typeof(this.colModel.config) == 'undefined') {
46211         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46212         this.cm = this.colModel;
46213         this.cm.xmodule = this.xmodule || false;
46214     }
46215     if (this.dataSource) {
46216         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46217         this.ds = this.dataSource;
46218         this.ds.xmodule = this.xmodule || false;
46219         
46220     }
46221     
46222     
46223     
46224     if(this.width){
46225         this.container.setWidth(this.width);
46226     }
46227
46228     if(this.height){
46229         this.container.setHeight(this.height);
46230     }
46231     /** @private */
46232         this.addEvents({
46233             // raw events
46234             /**
46235              * @event click
46236              * The raw click event for the entire grid.
46237              * @param {Roo.EventObject} e
46238              */
46239             "click" : true,
46240             /**
46241              * @event dblclick
46242              * The raw dblclick event for the entire grid.
46243              * @param {Roo.EventObject} e
46244              */
46245             "dblclick" : true,
46246             /**
46247              * @event contextmenu
46248              * The raw contextmenu event for the entire grid.
46249              * @param {Roo.EventObject} e
46250              */
46251             "contextmenu" : true,
46252             /**
46253              * @event mousedown
46254              * The raw mousedown event for the entire grid.
46255              * @param {Roo.EventObject} e
46256              */
46257             "mousedown" : true,
46258             /**
46259              * @event mouseup
46260              * The raw mouseup event for the entire grid.
46261              * @param {Roo.EventObject} e
46262              */
46263             "mouseup" : true,
46264             /**
46265              * @event mouseover
46266              * The raw mouseover event for the entire grid.
46267              * @param {Roo.EventObject} e
46268              */
46269             "mouseover" : true,
46270             /**
46271              * @event mouseout
46272              * The raw mouseout event for the entire grid.
46273              * @param {Roo.EventObject} e
46274              */
46275             "mouseout" : true,
46276             /**
46277              * @event keypress
46278              * The raw keypress event for the entire grid.
46279              * @param {Roo.EventObject} e
46280              */
46281             "keypress" : true,
46282             /**
46283              * @event keydown
46284              * The raw keydown event for the entire grid.
46285              * @param {Roo.EventObject} e
46286              */
46287             "keydown" : true,
46288
46289             // custom events
46290
46291             /**
46292              * @event cellclick
46293              * Fires when a cell is clicked
46294              * @param {Grid} this
46295              * @param {Number} rowIndex
46296              * @param {Number} columnIndex
46297              * @param {Roo.EventObject} e
46298              */
46299             "cellclick" : true,
46300             /**
46301              * @event celldblclick
46302              * Fires when a cell is double clicked
46303              * @param {Grid} this
46304              * @param {Number} rowIndex
46305              * @param {Number} columnIndex
46306              * @param {Roo.EventObject} e
46307              */
46308             "celldblclick" : true,
46309             /**
46310              * @event rowclick
46311              * Fires when a row is clicked
46312              * @param {Grid} this
46313              * @param {Number} rowIndex
46314              * @param {Roo.EventObject} e
46315              */
46316             "rowclick" : true,
46317             /**
46318              * @event rowdblclick
46319              * Fires when a row is double clicked
46320              * @param {Grid} this
46321              * @param {Number} rowIndex
46322              * @param {Roo.EventObject} e
46323              */
46324             "rowdblclick" : true,
46325             /**
46326              * @event headerclick
46327              * Fires when a header is clicked
46328              * @param {Grid} this
46329              * @param {Number} columnIndex
46330              * @param {Roo.EventObject} e
46331              */
46332             "headerclick" : true,
46333             /**
46334              * @event headerdblclick
46335              * Fires when a header cell is double clicked
46336              * @param {Grid} this
46337              * @param {Number} columnIndex
46338              * @param {Roo.EventObject} e
46339              */
46340             "headerdblclick" : true,
46341             /**
46342              * @event rowcontextmenu
46343              * Fires when a row is right clicked
46344              * @param {Grid} this
46345              * @param {Number} rowIndex
46346              * @param {Roo.EventObject} e
46347              */
46348             "rowcontextmenu" : true,
46349             /**
46350          * @event cellcontextmenu
46351          * Fires when a cell is right clicked
46352          * @param {Grid} this
46353          * @param {Number} rowIndex
46354          * @param {Number} cellIndex
46355          * @param {Roo.EventObject} e
46356          */
46357          "cellcontextmenu" : true,
46358             /**
46359              * @event headercontextmenu
46360              * Fires when a header is right clicked
46361              * @param {Grid} this
46362              * @param {Number} columnIndex
46363              * @param {Roo.EventObject} e
46364              */
46365             "headercontextmenu" : true,
46366             /**
46367              * @event bodyscroll
46368              * Fires when the body element is scrolled
46369              * @param {Number} scrollLeft
46370              * @param {Number} scrollTop
46371              */
46372             "bodyscroll" : true,
46373             /**
46374              * @event columnresize
46375              * Fires when the user resizes a column
46376              * @param {Number} columnIndex
46377              * @param {Number} newSize
46378              */
46379             "columnresize" : true,
46380             /**
46381              * @event columnmove
46382              * Fires when the user moves a column
46383              * @param {Number} oldIndex
46384              * @param {Number} newIndex
46385              */
46386             "columnmove" : true,
46387             /**
46388              * @event startdrag
46389              * Fires when row(s) start being dragged
46390              * @param {Grid} this
46391              * @param {Roo.GridDD} dd The drag drop object
46392              * @param {event} e The raw browser event
46393              */
46394             "startdrag" : true,
46395             /**
46396              * @event enddrag
46397              * Fires when a drag operation is complete
46398              * @param {Grid} this
46399              * @param {Roo.GridDD} dd The drag drop object
46400              * @param {event} e The raw browser event
46401              */
46402             "enddrag" : true,
46403             /**
46404              * @event dragdrop
46405              * Fires when dragged row(s) are dropped on a valid DD target
46406              * @param {Grid} this
46407              * @param {Roo.GridDD} dd The drag drop object
46408              * @param {String} targetId The target drag drop object
46409              * @param {event} e The raw browser event
46410              */
46411             "dragdrop" : true,
46412             /**
46413              * @event dragover
46414              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46415              * @param {Grid} this
46416              * @param {Roo.GridDD} dd The drag drop object
46417              * @param {String} targetId The target drag drop object
46418              * @param {event} e The raw browser event
46419              */
46420             "dragover" : true,
46421             /**
46422              * @event dragenter
46423              *  Fires when the dragged row(s) first cross another DD target while being dragged
46424              * @param {Grid} this
46425              * @param {Roo.GridDD} dd The drag drop object
46426              * @param {String} targetId The target drag drop object
46427              * @param {event} e The raw browser event
46428              */
46429             "dragenter" : true,
46430             /**
46431              * @event dragout
46432              * Fires when the dragged row(s) leave another DD target while being dragged
46433              * @param {Grid} this
46434              * @param {Roo.GridDD} dd The drag drop object
46435              * @param {String} targetId The target drag drop object
46436              * @param {event} e The raw browser event
46437              */
46438             "dragout" : true,
46439         /**
46440          * @event render
46441          * Fires when the grid is rendered
46442          * @param {Grid} grid
46443          */
46444         render : true
46445     });
46446
46447     Roo.grid.Grid.superclass.constructor.call(this);
46448 };
46449 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46450     
46451     /**
46452      * @cfg {String} ddGroup - drag drop group.
46453          */
46454     
46455     /**
46456      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46457          */
46458         minColumnWidth : 25,
46459
46460     /**
46461          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46462          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46463          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46464          */
46465         autoSizeColumns : false,
46466
46467         /**
46468          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46469          */
46470         autoSizeHeaders : true,
46471
46472         /**
46473          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46474          */
46475         monitorWindowResize : true,
46476
46477         /**
46478          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46479          * rows measured to get a columns size. Default is 0 (all rows).
46480          */
46481         maxRowsToMeasure : 0,
46482
46483         /**
46484          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46485          */
46486         trackMouseOver : true,
46487
46488     /**
46489          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46490          */
46491     
46492         /**
46493          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46494          */
46495         enableDragDrop : false,
46496
46497         /**
46498          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46499          */
46500         enableColumnMove : true,
46501
46502         /**
46503          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46504          */
46505         enableColumnHide : true,
46506
46507         /**
46508          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46509          */
46510         enableRowHeightSync : false,
46511
46512         /**
46513          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46514          */
46515         stripeRows : true,
46516
46517         /**
46518          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46519          */
46520         autoHeight : false,
46521
46522     /**
46523      * @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.
46524      */
46525     autoExpandColumn : false,
46526
46527     /**
46528     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46529     * Default is 50.
46530     */
46531     autoExpandMin : 50,
46532
46533     /**
46534     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46535     */
46536     autoExpandMax : 1000,
46537
46538     /**
46539          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46540          */
46541         view : null,
46542
46543         /**
46544      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46545          */
46546         loadMask : false,
46547     /**
46548      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46549          */
46550         dropTarget: false,
46551     // private
46552     rendered : false,
46553
46554     /**
46555     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46556     * of a fixed width. Default is false.
46557     */
46558     /**
46559     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46560     */
46561     /**
46562      * Called once after all setup has been completed and the grid is ready to be rendered.
46563      * @return {Roo.grid.Grid} this
46564      */
46565     render : function(){
46566         var c = this.container;
46567         // try to detect autoHeight/width mode
46568         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46569             this.autoHeight = true;
46570         }
46571         var view = this.getView();
46572         view.init(this);
46573
46574         c.on("click", this.onClick, this);
46575         c.on("dblclick", this.onDblClick, this);
46576         c.on("contextmenu", this.onContextMenu, this);
46577         c.on("keydown", this.onKeyDown, this);
46578
46579         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46580
46581         this.getSelectionModel().init(this);
46582
46583         view.render();
46584
46585         if(this.loadMask){
46586             this.loadMask = new Roo.LoadMask(this.container,
46587                     Roo.apply({store:this.dataSource}, this.loadMask));
46588         }
46589         
46590         
46591         if (this.toolbar && this.toolbar.xtype) {
46592             this.toolbar.container = this.getView().getHeaderPanel(true);
46593             this.toolbar = new Ext.Toolbar(this.toolbar);
46594         }
46595         if (this.footer && this.footer.xtype) {
46596             this.footer.dataSource = this.getDataSource();
46597             this.footer.container = this.getView().getFooterPanel(true);
46598             this.footer = Roo.factory(this.footer, Roo);
46599         }
46600         if (this.dropTarget && this.dropTarget.xtype) {
46601             delete this.dropTarget.xtype;
46602             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46603         }
46604         
46605         
46606         this.rendered = true;
46607         this.fireEvent('render', this);
46608         return this;
46609     },
46610
46611         /**
46612          * Reconfigures the grid to use a different Store and Column Model.
46613          * The View will be bound to the new objects and refreshed.
46614          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46615          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46616          */
46617     reconfigure : function(dataSource, colModel){
46618         if(this.loadMask){
46619             this.loadMask.destroy();
46620             this.loadMask = new Roo.LoadMask(this.container,
46621                     Roo.apply({store:dataSource}, this.loadMask));
46622         }
46623         this.view.bind(dataSource, colModel);
46624         this.dataSource = dataSource;
46625         this.colModel = colModel;
46626         this.view.refresh(true);
46627     },
46628
46629     // private
46630     onKeyDown : function(e){
46631         this.fireEvent("keydown", e);
46632     },
46633
46634     /**
46635      * Destroy this grid.
46636      * @param {Boolean} removeEl True to remove the element
46637      */
46638     destroy : function(removeEl, keepListeners){
46639         if(this.loadMask){
46640             this.loadMask.destroy();
46641         }
46642         var c = this.container;
46643         c.removeAllListeners();
46644         this.view.destroy();
46645         this.colModel.purgeListeners();
46646         if(!keepListeners){
46647             this.purgeListeners();
46648         }
46649         c.update("");
46650         if(removeEl === true){
46651             c.remove();
46652         }
46653     },
46654
46655     // private
46656     processEvent : function(name, e){
46657         this.fireEvent(name, e);
46658         var t = e.getTarget();
46659         var v = this.view;
46660         var header = v.findHeaderIndex(t);
46661         if(header !== false){
46662             this.fireEvent("header" + name, this, header, e);
46663         }else{
46664             var row = v.findRowIndex(t);
46665             var cell = v.findCellIndex(t);
46666             if(row !== false){
46667                 this.fireEvent("row" + name, this, row, e);
46668                 if(cell !== false){
46669                     this.fireEvent("cell" + name, this, row, cell, e);
46670                 }
46671             }
46672         }
46673     },
46674
46675     // private
46676     onClick : function(e){
46677         this.processEvent("click", e);
46678     },
46679
46680     // private
46681     onContextMenu : function(e, t){
46682         this.processEvent("contextmenu", e);
46683     },
46684
46685     // private
46686     onDblClick : function(e){
46687         this.processEvent("dblclick", e);
46688     },
46689
46690     // private
46691     walkCells : function(row, col, step, fn, scope){
46692         var cm = this.colModel, clen = cm.getColumnCount();
46693         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46694         if(step < 0){
46695             if(col < 0){
46696                 row--;
46697                 first = false;
46698             }
46699             while(row >= 0){
46700                 if(!first){
46701                     col = clen-1;
46702                 }
46703                 first = false;
46704                 while(col >= 0){
46705                     if(fn.call(scope || this, row, col, cm) === true){
46706                         return [row, col];
46707                     }
46708                     col--;
46709                 }
46710                 row--;
46711             }
46712         } else {
46713             if(col >= clen){
46714                 row++;
46715                 first = false;
46716             }
46717             while(row < rlen){
46718                 if(!first){
46719                     col = 0;
46720                 }
46721                 first = false;
46722                 while(col < clen){
46723                     if(fn.call(scope || this, row, col, cm) === true){
46724                         return [row, col];
46725                     }
46726                     col++;
46727                 }
46728                 row++;
46729             }
46730         }
46731         return null;
46732     },
46733
46734     // private
46735     getSelections : function(){
46736         return this.selModel.getSelections();
46737     },
46738
46739     /**
46740      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46741      * but if manual update is required this method will initiate it.
46742      */
46743     autoSize : function(){
46744         if(this.rendered){
46745             this.view.layout();
46746             if(this.view.adjustForScroll){
46747                 this.view.adjustForScroll();
46748             }
46749         }
46750     },
46751
46752     /**
46753      * Returns the grid's underlying element.
46754      * @return {Element} The element
46755      */
46756     getGridEl : function(){
46757         return this.container;
46758     },
46759
46760     // private for compatibility, overridden by editor grid
46761     stopEditing : function(){},
46762
46763     /**
46764      * Returns the grid's SelectionModel.
46765      * @return {SelectionModel}
46766      */
46767     getSelectionModel : function(){
46768         if(!this.selModel){
46769             this.selModel = new Roo.grid.RowSelectionModel();
46770         }
46771         return this.selModel;
46772     },
46773
46774     /**
46775      * Returns the grid's DataSource.
46776      * @return {DataSource}
46777      */
46778     getDataSource : function(){
46779         return this.dataSource;
46780     },
46781
46782     /**
46783      * Returns the grid's ColumnModel.
46784      * @return {ColumnModel}
46785      */
46786     getColumnModel : function(){
46787         return this.colModel;
46788     },
46789
46790     /**
46791      * Returns the grid's GridView object.
46792      * @return {GridView}
46793      */
46794     getView : function(){
46795         if(!this.view){
46796             this.view = new Roo.grid.GridView(this.viewConfig);
46797         }
46798         return this.view;
46799     },
46800     /**
46801      * Called to get grid's drag proxy text, by default returns this.ddText.
46802      * @return {String}
46803      */
46804     getDragDropText : function(){
46805         var count = this.selModel.getCount();
46806         return String.format(this.ddText, count, count == 1 ? '' : 's');
46807     }
46808 });
46809 /**
46810  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46811  * %0 is replaced with the number of selected rows.
46812  * @type String
46813  */
46814 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46815  * Based on:
46816  * Ext JS Library 1.1.1
46817  * Copyright(c) 2006-2007, Ext JS, LLC.
46818  *
46819  * Originally Released Under LGPL - original licence link has changed is not relivant.
46820  *
46821  * Fork - LGPL
46822  * <script type="text/javascript">
46823  */
46824  
46825 Roo.grid.AbstractGridView = function(){
46826         this.grid = null;
46827         
46828         this.events = {
46829             "beforerowremoved" : true,
46830             "beforerowsinserted" : true,
46831             "beforerefresh" : true,
46832             "rowremoved" : true,
46833             "rowsinserted" : true,
46834             "rowupdated" : true,
46835             "refresh" : true
46836         };
46837     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46838 };
46839
46840 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46841     rowClass : "x-grid-row",
46842     cellClass : "x-grid-cell",
46843     tdClass : "x-grid-td",
46844     hdClass : "x-grid-hd",
46845     splitClass : "x-grid-hd-split",
46846     
46847         init: function(grid){
46848         this.grid = grid;
46849                 var cid = this.grid.getGridEl().id;
46850         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46851         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46852         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46853         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46854         },
46855         
46856         getColumnRenderers : function(){
46857         var renderers = [];
46858         var cm = this.grid.colModel;
46859         var colCount = cm.getColumnCount();
46860         for(var i = 0; i < colCount; i++){
46861             renderers[i] = cm.getRenderer(i);
46862         }
46863         return renderers;
46864     },
46865     
46866     getColumnIds : function(){
46867         var ids = [];
46868         var cm = this.grid.colModel;
46869         var colCount = cm.getColumnCount();
46870         for(var i = 0; i < colCount; i++){
46871             ids[i] = cm.getColumnId(i);
46872         }
46873         return ids;
46874     },
46875     
46876     getDataIndexes : function(){
46877         if(!this.indexMap){
46878             this.indexMap = this.buildIndexMap();
46879         }
46880         return this.indexMap.colToData;
46881     },
46882     
46883     getColumnIndexByDataIndex : function(dataIndex){
46884         if(!this.indexMap){
46885             this.indexMap = this.buildIndexMap();
46886         }
46887         return this.indexMap.dataToCol[dataIndex];
46888     },
46889     
46890     /**
46891      * Set a css style for a column dynamically. 
46892      * @param {Number} colIndex The index of the column
46893      * @param {String} name The css property name
46894      * @param {String} value The css value
46895      */
46896     setCSSStyle : function(colIndex, name, value){
46897         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46898         Roo.util.CSS.updateRule(selector, name, value);
46899     },
46900     
46901     generateRules : function(cm){
46902         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46903         Roo.util.CSS.removeStyleSheet(rulesId);
46904         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46905             var cid = cm.getColumnId(i);
46906             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46907                          this.tdSelector, cid, " {\n}\n",
46908                          this.hdSelector, cid, " {\n}\n",
46909                          this.splitSelector, cid, " {\n}\n");
46910         }
46911         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46912     }
46913 });/*
46914  * Based on:
46915  * Ext JS Library 1.1.1
46916  * Copyright(c) 2006-2007, Ext JS, LLC.
46917  *
46918  * Originally Released Under LGPL - original licence link has changed is not relivant.
46919  *
46920  * Fork - LGPL
46921  * <script type="text/javascript">
46922  */
46923
46924 // private
46925 // This is a support class used internally by the Grid components
46926 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46927     this.grid = grid;
46928     this.view = grid.getView();
46929     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46930     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46931     if(hd2){
46932         this.setHandleElId(Roo.id(hd));
46933         this.setOuterHandleElId(Roo.id(hd2));
46934     }
46935     this.scroll = false;
46936 };
46937 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46938     maxDragWidth: 120,
46939     getDragData : function(e){
46940         var t = Roo.lib.Event.getTarget(e);
46941         var h = this.view.findHeaderCell(t);
46942         if(h){
46943             return {ddel: h.firstChild, header:h};
46944         }
46945         return false;
46946     },
46947
46948     onInitDrag : function(e){
46949         this.view.headersDisabled = true;
46950         var clone = this.dragData.ddel.cloneNode(true);
46951         clone.id = Roo.id();
46952         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46953         this.proxy.update(clone);
46954         return true;
46955     },
46956
46957     afterValidDrop : function(){
46958         var v = this.view;
46959         setTimeout(function(){
46960             v.headersDisabled = false;
46961         }, 50);
46962     },
46963
46964     afterInvalidDrop : function(){
46965         var v = this.view;
46966         setTimeout(function(){
46967             v.headersDisabled = false;
46968         }, 50);
46969     }
46970 });
46971 /*
46972  * Based on:
46973  * Ext JS Library 1.1.1
46974  * Copyright(c) 2006-2007, Ext JS, LLC.
46975  *
46976  * Originally Released Under LGPL - original licence link has changed is not relivant.
46977  *
46978  * Fork - LGPL
46979  * <script type="text/javascript">
46980  */
46981 // private
46982 // This is a support class used internally by the Grid components
46983 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46984     this.grid = grid;
46985     this.view = grid.getView();
46986     // split the proxies so they don't interfere with mouse events
46987     this.proxyTop = Roo.DomHelper.append(document.body, {
46988         cls:"col-move-top", html:"&#160;"
46989     }, true);
46990     this.proxyBottom = Roo.DomHelper.append(document.body, {
46991         cls:"col-move-bottom", html:"&#160;"
46992     }, true);
46993     this.proxyTop.hide = this.proxyBottom.hide = function(){
46994         this.setLeftTop(-100,-100);
46995         this.setStyle("visibility", "hidden");
46996     };
46997     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46998     // temporarily disabled
46999     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47000     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47001 };
47002 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47003     proxyOffsets : [-4, -9],
47004     fly: Roo.Element.fly,
47005
47006     getTargetFromEvent : function(e){
47007         var t = Roo.lib.Event.getTarget(e);
47008         var cindex = this.view.findCellIndex(t);
47009         if(cindex !== false){
47010             return this.view.getHeaderCell(cindex);
47011         }
47012     },
47013
47014     nextVisible : function(h){
47015         var v = this.view, cm = this.grid.colModel;
47016         h = h.nextSibling;
47017         while(h){
47018             if(!cm.isHidden(v.getCellIndex(h))){
47019                 return h;
47020             }
47021             h = h.nextSibling;
47022         }
47023         return null;
47024     },
47025
47026     prevVisible : function(h){
47027         var v = this.view, cm = this.grid.colModel;
47028         h = h.prevSibling;
47029         while(h){
47030             if(!cm.isHidden(v.getCellIndex(h))){
47031                 return h;
47032             }
47033             h = h.prevSibling;
47034         }
47035         return null;
47036     },
47037
47038     positionIndicator : function(h, n, e){
47039         var x = Roo.lib.Event.getPageX(e);
47040         var r = Roo.lib.Dom.getRegion(n.firstChild);
47041         var px, pt, py = r.top + this.proxyOffsets[1];
47042         if((r.right - x) <= (r.right-r.left)/2){
47043             px = r.right+this.view.borderWidth;
47044             pt = "after";
47045         }else{
47046             px = r.left;
47047             pt = "before";
47048         }
47049         var oldIndex = this.view.getCellIndex(h);
47050         var newIndex = this.view.getCellIndex(n);
47051
47052         if(this.grid.colModel.isFixed(newIndex)){
47053             return false;
47054         }
47055
47056         var locked = this.grid.colModel.isLocked(newIndex);
47057
47058         if(pt == "after"){
47059             newIndex++;
47060         }
47061         if(oldIndex < newIndex){
47062             newIndex--;
47063         }
47064         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47065             return false;
47066         }
47067         px +=  this.proxyOffsets[0];
47068         this.proxyTop.setLeftTop(px, py);
47069         this.proxyTop.show();
47070         if(!this.bottomOffset){
47071             this.bottomOffset = this.view.mainHd.getHeight();
47072         }
47073         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47074         this.proxyBottom.show();
47075         return pt;
47076     },
47077
47078     onNodeEnter : function(n, dd, e, data){
47079         if(data.header != n){
47080             this.positionIndicator(data.header, n, e);
47081         }
47082     },
47083
47084     onNodeOver : function(n, dd, e, data){
47085         var result = false;
47086         if(data.header != n){
47087             result = this.positionIndicator(data.header, n, e);
47088         }
47089         if(!result){
47090             this.proxyTop.hide();
47091             this.proxyBottom.hide();
47092         }
47093         return result ? this.dropAllowed : this.dropNotAllowed;
47094     },
47095
47096     onNodeOut : function(n, dd, e, data){
47097         this.proxyTop.hide();
47098         this.proxyBottom.hide();
47099     },
47100
47101     onNodeDrop : function(n, dd, e, data){
47102         var h = data.header;
47103         if(h != n){
47104             var cm = this.grid.colModel;
47105             var x = Roo.lib.Event.getPageX(e);
47106             var r = Roo.lib.Dom.getRegion(n.firstChild);
47107             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47108             var oldIndex = this.view.getCellIndex(h);
47109             var newIndex = this.view.getCellIndex(n);
47110             var locked = cm.isLocked(newIndex);
47111             if(pt == "after"){
47112                 newIndex++;
47113             }
47114             if(oldIndex < newIndex){
47115                 newIndex--;
47116             }
47117             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47118                 return false;
47119             }
47120             cm.setLocked(oldIndex, locked, true);
47121             cm.moveColumn(oldIndex, newIndex);
47122             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47123             return true;
47124         }
47125         return false;
47126     }
47127 });
47128 /*
47129  * Based on:
47130  * Ext JS Library 1.1.1
47131  * Copyright(c) 2006-2007, Ext JS, LLC.
47132  *
47133  * Originally Released Under LGPL - original licence link has changed is not relivant.
47134  *
47135  * Fork - LGPL
47136  * <script type="text/javascript">
47137  */
47138   
47139 /**
47140  * @class Roo.grid.GridView
47141  * @extends Roo.util.Observable
47142  *
47143  * @constructor
47144  * @param {Object} config
47145  */
47146 Roo.grid.GridView = function(config){
47147     Roo.grid.GridView.superclass.constructor.call(this);
47148     this.el = null;
47149
47150     Roo.apply(this, config);
47151 };
47152
47153 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47154
47155     /**
47156      * Override this function to apply custom css classes to rows during rendering
47157      * @param {Record} record The record
47158      * @param {Number} index
47159      * @method getRowClass
47160      */
47161     rowClass : "x-grid-row",
47162
47163     cellClass : "x-grid-col",
47164
47165     tdClass : "x-grid-td",
47166
47167     hdClass : "x-grid-hd",
47168
47169     splitClass : "x-grid-split",
47170
47171     sortClasses : ["sort-asc", "sort-desc"],
47172
47173     enableMoveAnim : false,
47174
47175     hlColor: "C3DAF9",
47176
47177     dh : Roo.DomHelper,
47178
47179     fly : Roo.Element.fly,
47180
47181     css : Roo.util.CSS,
47182
47183     borderWidth: 1,
47184
47185     splitOffset: 3,
47186
47187     scrollIncrement : 22,
47188
47189     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47190
47191     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47192
47193     bind : function(ds, cm){
47194         if(this.ds){
47195             this.ds.un("load", this.onLoad, this);
47196             this.ds.un("datachanged", this.onDataChange, this);
47197             this.ds.un("add", this.onAdd, this);
47198             this.ds.un("remove", this.onRemove, this);
47199             this.ds.un("update", this.onUpdate, this);
47200             this.ds.un("clear", this.onClear, this);
47201         }
47202         if(ds){
47203             ds.on("load", this.onLoad, this);
47204             ds.on("datachanged", this.onDataChange, this);
47205             ds.on("add", this.onAdd, this);
47206             ds.on("remove", this.onRemove, this);
47207             ds.on("update", this.onUpdate, this);
47208             ds.on("clear", this.onClear, this);
47209         }
47210         this.ds = ds;
47211
47212         if(this.cm){
47213             this.cm.un("widthchange", this.onColWidthChange, this);
47214             this.cm.un("headerchange", this.onHeaderChange, this);
47215             this.cm.un("hiddenchange", this.onHiddenChange, this);
47216             this.cm.un("columnmoved", this.onColumnMove, this);
47217             this.cm.un("columnlockchange", this.onColumnLock, this);
47218         }
47219         if(cm){
47220             this.generateRules(cm);
47221             cm.on("widthchange", this.onColWidthChange, this);
47222             cm.on("headerchange", this.onHeaderChange, this);
47223             cm.on("hiddenchange", this.onHiddenChange, this);
47224             cm.on("columnmoved", this.onColumnMove, this);
47225             cm.on("columnlockchange", this.onColumnLock, this);
47226         }
47227         this.cm = cm;
47228     },
47229
47230     init: function(grid){
47231                 Roo.grid.GridView.superclass.init.call(this, grid);
47232
47233                 this.bind(grid.dataSource, grid.colModel);
47234
47235             grid.on("headerclick", this.handleHeaderClick, this);
47236
47237         if(grid.trackMouseOver){
47238             grid.on("mouseover", this.onRowOver, this);
47239                 grid.on("mouseout", this.onRowOut, this);
47240             }
47241             grid.cancelTextSelection = function(){};
47242                 this.gridId = grid.id;
47243
47244                 var tpls = this.templates || {};
47245
47246                 if(!tpls.master){
47247                     tpls.master = new Roo.Template(
47248                        '<div class="x-grid" hidefocus="true">',
47249                           '<div class="x-grid-topbar"></div>',
47250                           '<div class="x-grid-scroller"><div></div></div>',
47251                           '<div class="x-grid-locked">',
47252                               '<div class="x-grid-header">{lockedHeader}</div>',
47253                               '<div class="x-grid-body">{lockedBody}</div>',
47254                           "</div>",
47255                           '<div class="x-grid-viewport">',
47256                               '<div class="x-grid-header">{header}</div>',
47257                               '<div class="x-grid-body">{body}</div>',
47258                           "</div>",
47259                           '<div class="x-grid-bottombar"></div>',
47260                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47261                           '<div class="x-grid-resize-proxy">&#160;</div>',
47262                        "</div>"
47263                     );
47264                     tpls.master.disableformats = true;
47265                 }
47266
47267                 if(!tpls.header){
47268                     tpls.header = new Roo.Template(
47269                        '<table border="0" cellspacing="0" cellpadding="0">',
47270                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47271                        "</table>{splits}"
47272                     );
47273                     tpls.header.disableformats = true;
47274                 }
47275                 tpls.header.compile();
47276
47277                 if(!tpls.hcell){
47278                     tpls.hcell = new Roo.Template(
47279                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47280                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47281                         "</div></td>"
47282                      );
47283                      tpls.hcell.disableFormats = true;
47284                 }
47285                 tpls.hcell.compile();
47286
47287                 if(!tpls.hsplit){
47288                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47289                     tpls.hsplit.disableFormats = true;
47290                 }
47291                 tpls.hsplit.compile();
47292
47293                 if(!tpls.body){
47294                     tpls.body = new Roo.Template(
47295                        '<table border="0" cellspacing="0" cellpadding="0">',
47296                        "<tbody>{rows}</tbody>",
47297                        "</table>"
47298                     );
47299                     tpls.body.disableFormats = true;
47300                 }
47301                 tpls.body.compile();
47302
47303                 if(!tpls.row){
47304                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47305                     tpls.row.disableFormats = true;
47306                 }
47307                 tpls.row.compile();
47308
47309                 if(!tpls.cell){
47310                     tpls.cell = new Roo.Template(
47311                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47312                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47313                         "</td>"
47314                     );
47315             tpls.cell.disableFormats = true;
47316         }
47317                 tpls.cell.compile();
47318
47319                 this.templates = tpls;
47320         },
47321
47322         // remap these for backwards compat
47323     onColWidthChange : function(){
47324         this.updateColumns.apply(this, arguments);
47325     },
47326     onHeaderChange : function(){
47327         this.updateHeaders.apply(this, arguments);
47328     }, 
47329     onHiddenChange : function(){
47330         this.handleHiddenChange.apply(this, arguments);
47331     },
47332     onColumnMove : function(){
47333         this.handleColumnMove.apply(this, arguments);
47334     },
47335     onColumnLock : function(){
47336         this.handleLockChange.apply(this, arguments);
47337     },
47338
47339     onDataChange : function(){
47340         this.refresh();
47341         this.updateHeaderSortState();
47342     },
47343
47344         onClear : function(){
47345         this.refresh();
47346     },
47347
47348         onUpdate : function(ds, record){
47349         this.refreshRow(record);
47350     },
47351
47352     refreshRow : function(record){
47353         var ds = this.ds, index;
47354         if(typeof record == 'number'){
47355             index = record;
47356             record = ds.getAt(index);
47357         }else{
47358             index = ds.indexOf(record);
47359         }
47360         this.insertRows(ds, index, index, true);
47361         this.onRemove(ds, record, index+1, true);
47362         this.syncRowHeights(index, index);
47363         this.layout();
47364         this.fireEvent("rowupdated", this, index, record);
47365     },
47366
47367     onAdd : function(ds, records, index){
47368         this.insertRows(ds, index, index + (records.length-1));
47369     },
47370
47371     onRemove : function(ds, record, index, isUpdate){
47372         if(isUpdate !== true){
47373             this.fireEvent("beforerowremoved", this, index, record);
47374         }
47375         var bt = this.getBodyTable(), lt = this.getLockedTable();
47376         if(bt.rows[index]){
47377             bt.firstChild.removeChild(bt.rows[index]);
47378         }
47379         if(lt.rows[index]){
47380             lt.firstChild.removeChild(lt.rows[index]);
47381         }
47382         if(isUpdate !== true){
47383             this.stripeRows(index);
47384             this.syncRowHeights(index, index);
47385             this.layout();
47386             this.fireEvent("rowremoved", this, index, record);
47387         }
47388     },
47389
47390     onLoad : function(){
47391         this.scrollToTop();
47392     },
47393
47394     /**
47395      * Scrolls the grid to the top
47396      */
47397     scrollToTop : function(){
47398         if(this.scroller){
47399             this.scroller.dom.scrollTop = 0;
47400             this.syncScroll();
47401         }
47402     },
47403
47404     /**
47405      * Gets a panel in the header of the grid that can be used for toolbars etc.
47406      * After modifying the contents of this panel a call to grid.autoSize() may be
47407      * required to register any changes in size.
47408      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47409      * @return Roo.Element
47410      */
47411     getHeaderPanel : function(doShow){
47412         if(doShow){
47413             this.headerPanel.show();
47414         }
47415         return this.headerPanel;
47416         },
47417
47418         /**
47419      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47420      * After modifying the contents of this panel a call to grid.autoSize() may be
47421      * required to register any changes in size.
47422      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47423      * @return Roo.Element
47424      */
47425     getFooterPanel : function(doShow){
47426         if(doShow){
47427             this.footerPanel.show();
47428         }
47429         return this.footerPanel;
47430         },
47431
47432         initElements : function(){
47433             var E = Roo.Element;
47434             var el = this.grid.getGridEl().dom.firstChild;
47435             var cs = el.childNodes;
47436
47437             this.el = new E(el);
47438             this.headerPanel = new E(el.firstChild);
47439             this.headerPanel.enableDisplayMode("block");
47440
47441         this.scroller = new E(cs[1]);
47442             this.scrollSizer = new E(this.scroller.dom.firstChild);
47443
47444             this.lockedWrap = new E(cs[2]);
47445             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47446             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47447
47448             this.mainWrap = new E(cs[3]);
47449             this.mainHd = new E(this.mainWrap.dom.firstChild);
47450             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47451
47452             this.footerPanel = new E(cs[4]);
47453             this.footerPanel.enableDisplayMode("block");
47454
47455         this.focusEl = new E(cs[5]);
47456         this.focusEl.swallowEvent("click", true);
47457         this.resizeProxy = new E(cs[6]);
47458
47459             this.headerSelector = String.format(
47460                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47461                this.lockedHd.id, this.mainHd.id
47462             );
47463
47464             this.splitterSelector = String.format(
47465                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47466                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47467             );
47468     },
47469     idToCssName : function(s)
47470     {
47471         return s.replace(/[^a-z0-9]+/ig, '-');
47472     },
47473
47474         getHeaderCell : function(index){
47475             return Roo.DomQuery.select(this.headerSelector)[index];
47476         },
47477
47478         getHeaderCellMeasure : function(index){
47479             return this.getHeaderCell(index).firstChild;
47480         },
47481
47482         getHeaderCellText : function(index){
47483             return this.getHeaderCell(index).firstChild.firstChild;
47484         },
47485
47486         getLockedTable : function(){
47487             return this.lockedBody.dom.firstChild;
47488         },
47489
47490         getBodyTable : function(){
47491             return this.mainBody.dom.firstChild;
47492         },
47493
47494         getLockedRow : function(index){
47495             return this.getLockedTable().rows[index];
47496         },
47497
47498         getRow : function(index){
47499             return this.getBodyTable().rows[index];
47500         },
47501
47502         getRowComposite : function(index){
47503             if(!this.rowEl){
47504                 this.rowEl = new Roo.CompositeElementLite();
47505             }
47506         var els = [], lrow, mrow;
47507         if(lrow = this.getLockedRow(index)){
47508             els.push(lrow);
47509         }
47510         if(mrow = this.getRow(index)){
47511             els.push(mrow);
47512         }
47513         this.rowEl.elements = els;
47514             return this.rowEl;
47515         },
47516
47517         getCell : function(rowIndex, colIndex){
47518             var locked = this.cm.getLockedCount();
47519             var source;
47520             if(colIndex < locked){
47521                 source = this.lockedBody.dom.firstChild;
47522             }else{
47523                 source = this.mainBody.dom.firstChild;
47524                 colIndex -= locked;
47525             }
47526         return source.rows[rowIndex].childNodes[colIndex];
47527         },
47528
47529         getCellText : function(rowIndex, colIndex){
47530             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47531         },
47532
47533         getCellBox : function(cell){
47534             var b = this.fly(cell).getBox();
47535         if(Roo.isOpera){ // opera fails to report the Y
47536             b.y = cell.offsetTop + this.mainBody.getY();
47537         }
47538         return b;
47539     },
47540
47541     getCellIndex : function(cell){
47542         var id = String(cell.className).match(this.cellRE);
47543         if(id){
47544             return parseInt(id[1], 10);
47545         }
47546         return 0;
47547     },
47548
47549     findHeaderIndex : function(n){
47550         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47551         return r ? this.getCellIndex(r) : false;
47552     },
47553
47554     findHeaderCell : function(n){
47555         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47556         return r ? r : false;
47557     },
47558
47559     findRowIndex : function(n){
47560         if(!n){
47561             return false;
47562         }
47563         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47564         return r ? r.rowIndex : false;
47565     },
47566
47567     findCellIndex : function(node){
47568         var stop = this.el.dom;
47569         while(node && node != stop){
47570             if(this.findRE.test(node.className)){
47571                 return this.getCellIndex(node);
47572             }
47573             node = node.parentNode;
47574         }
47575         return false;
47576     },
47577
47578     getColumnId : function(index){
47579             return this.cm.getColumnId(index);
47580         },
47581
47582         getSplitters : function(){
47583             if(this.splitterSelector){
47584                return Roo.DomQuery.select(this.splitterSelector);
47585             }else{
47586                 return null;
47587             }
47588         },
47589
47590         getSplitter : function(index){
47591             return this.getSplitters()[index];
47592         },
47593
47594     onRowOver : function(e, t){
47595         var row;
47596         if((row = this.findRowIndex(t)) !== false){
47597             this.getRowComposite(row).addClass("x-grid-row-over");
47598         }
47599     },
47600
47601     onRowOut : function(e, t){
47602         var row;
47603         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47604             this.getRowComposite(row).removeClass("x-grid-row-over");
47605         }
47606     },
47607
47608     renderHeaders : function(){
47609             var cm = this.cm;
47610         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47611         var cb = [], lb = [], sb = [], lsb = [], p = {};
47612         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47613             p.cellId = "x-grid-hd-0-" + i;
47614             p.splitId = "x-grid-csplit-0-" + i;
47615             p.id = cm.getColumnId(i);
47616             p.title = cm.getColumnTooltip(i) || "";
47617             p.value = cm.getColumnHeader(i) || "";
47618             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47619             if(!cm.isLocked(i)){
47620                 cb[cb.length] = ct.apply(p);
47621                 sb[sb.length] = st.apply(p);
47622             }else{
47623                 lb[lb.length] = ct.apply(p);
47624                 lsb[lsb.length] = st.apply(p);
47625             }
47626         }
47627         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47628                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47629         },
47630
47631         updateHeaders : function(){
47632         var html = this.renderHeaders();
47633         this.lockedHd.update(html[0]);
47634         this.mainHd.update(html[1]);
47635     },
47636
47637     /**
47638      * Focuses the specified row.
47639      * @param {Number} row The row index
47640      */
47641     focusRow : function(row){
47642         var x = this.scroller.dom.scrollLeft;
47643         this.focusCell(row, 0, false);
47644         this.scroller.dom.scrollLeft = x;
47645     },
47646
47647     /**
47648      * Focuses the specified cell.
47649      * @param {Number} row The row index
47650      * @param {Number} col The column index
47651      * @param {Boolean} hscroll false to disable horizontal scrolling
47652      */
47653     focusCell : function(row, col, hscroll){
47654         var el = this.ensureVisible(row, col, hscroll);
47655         this.focusEl.alignTo(el, "tl-tl");
47656         if(Roo.isGecko){
47657             this.focusEl.focus();
47658         }else{
47659             this.focusEl.focus.defer(1, this.focusEl);
47660         }
47661     },
47662
47663     /**
47664      * Scrolls the specified cell into view
47665      * @param {Number} row The row index
47666      * @param {Number} col The column index
47667      * @param {Boolean} hscroll false to disable horizontal scrolling
47668      */
47669     ensureVisible : function(row, col, hscroll){
47670         if(typeof row != "number"){
47671             row = row.rowIndex;
47672         }
47673         if(row < 0 && row >= this.ds.getCount()){
47674             return;
47675         }
47676         col = (col !== undefined ? col : 0);
47677         var cm = this.grid.colModel;
47678         while(cm.isHidden(col)){
47679             col++;
47680         }
47681
47682         var el = this.getCell(row, col);
47683         if(!el){
47684             return;
47685         }
47686         var c = this.scroller.dom;
47687
47688         var ctop = parseInt(el.offsetTop, 10);
47689         var cleft = parseInt(el.offsetLeft, 10);
47690         var cbot = ctop + el.offsetHeight;
47691         var cright = cleft + el.offsetWidth;
47692
47693         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47694         var stop = parseInt(c.scrollTop, 10);
47695         var sleft = parseInt(c.scrollLeft, 10);
47696         var sbot = stop + ch;
47697         var sright = sleft + c.clientWidth;
47698
47699         if(ctop < stop){
47700                 c.scrollTop = ctop;
47701         }else if(cbot > sbot){
47702             c.scrollTop = cbot-ch;
47703         }
47704
47705         if(hscroll !== false){
47706             if(cleft < sleft){
47707                 c.scrollLeft = cleft;
47708             }else if(cright > sright){
47709                 c.scrollLeft = cright-c.clientWidth;
47710             }
47711         }
47712         return el;
47713     },
47714
47715     updateColumns : function(){
47716         this.grid.stopEditing();
47717         var cm = this.grid.colModel, colIds = this.getColumnIds();
47718         //var totalWidth = cm.getTotalWidth();
47719         var pos = 0;
47720         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47721             //if(cm.isHidden(i)) continue;
47722             var w = cm.getColumnWidth(i);
47723             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47724             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47725         }
47726         this.updateSplitters();
47727     },
47728
47729     generateRules : function(cm){
47730         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47731         Roo.util.CSS.removeStyleSheet(rulesId);
47732         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47733             var cid = cm.getColumnId(i);
47734             var align = '';
47735             if(cm.config[i].align){
47736                 align = 'text-align:'+cm.config[i].align+';';
47737             }
47738             var hidden = '';
47739             if(cm.isHidden(i)){
47740                 hidden = 'display:none;';
47741             }
47742             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47743             ruleBuf.push(
47744                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47745                     this.hdSelector, cid, " {\n", align, width, "}\n",
47746                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47747                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47748         }
47749         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47750     },
47751
47752     updateSplitters : function(){
47753         var cm = this.cm, s = this.getSplitters();
47754         if(s){ // splitters not created yet
47755             var pos = 0, locked = true;
47756             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47757                 if(cm.isHidden(i)) continue;
47758                 var w = cm.getColumnWidth(i);
47759                 if(!cm.isLocked(i) && locked){
47760                     pos = 0;
47761                     locked = false;
47762                 }
47763                 pos += w;
47764                 s[i].style.left = (pos-this.splitOffset) + "px";
47765             }
47766         }
47767     },
47768
47769     handleHiddenChange : function(colModel, colIndex, hidden){
47770         if(hidden){
47771             this.hideColumn(colIndex);
47772         }else{
47773             this.unhideColumn(colIndex);
47774         }
47775     },
47776
47777     hideColumn : function(colIndex){
47778         var cid = this.getColumnId(colIndex);
47779         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47780         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47781         if(Roo.isSafari){
47782             this.updateHeaders();
47783         }
47784         this.updateSplitters();
47785         this.layout();
47786     },
47787
47788     unhideColumn : function(colIndex){
47789         var cid = this.getColumnId(colIndex);
47790         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47791         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47792
47793         if(Roo.isSafari){
47794             this.updateHeaders();
47795         }
47796         this.updateSplitters();
47797         this.layout();
47798     },
47799
47800     insertRows : function(dm, firstRow, lastRow, isUpdate){
47801         if(firstRow == 0 && lastRow == dm.getCount()-1){
47802             this.refresh();
47803         }else{
47804             if(!isUpdate){
47805                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47806             }
47807             var s = this.getScrollState();
47808             var markup = this.renderRows(firstRow, lastRow);
47809             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47810             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47811             this.restoreScroll(s);
47812             if(!isUpdate){
47813                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47814                 this.syncRowHeights(firstRow, lastRow);
47815                 this.stripeRows(firstRow);
47816                 this.layout();
47817             }
47818         }
47819     },
47820
47821     bufferRows : function(markup, target, index){
47822         var before = null, trows = target.rows, tbody = target.tBodies[0];
47823         if(index < trows.length){
47824             before = trows[index];
47825         }
47826         var b = document.createElement("div");
47827         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47828         var rows = b.firstChild.rows;
47829         for(var i = 0, len = rows.length; i < len; i++){
47830             if(before){
47831                 tbody.insertBefore(rows[0], before);
47832             }else{
47833                 tbody.appendChild(rows[0]);
47834             }
47835         }
47836         b.innerHTML = "";
47837         b = null;
47838     },
47839
47840     deleteRows : function(dm, firstRow, lastRow){
47841         if(dm.getRowCount()<1){
47842             this.fireEvent("beforerefresh", this);
47843             this.mainBody.update("");
47844             this.lockedBody.update("");
47845             this.fireEvent("refresh", this);
47846         }else{
47847             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47848             var bt = this.getBodyTable();
47849             var tbody = bt.firstChild;
47850             var rows = bt.rows;
47851             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47852                 tbody.removeChild(rows[firstRow]);
47853             }
47854             this.stripeRows(firstRow);
47855             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47856         }
47857     },
47858
47859     updateRows : function(dataSource, firstRow, lastRow){
47860         var s = this.getScrollState();
47861         this.refresh();
47862         this.restoreScroll(s);
47863     },
47864
47865     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47866         if(!noRefresh){
47867            this.refresh();
47868         }
47869         this.updateHeaderSortState();
47870     },
47871
47872     getScrollState : function(){
47873         var sb = this.scroller.dom;
47874         return {left: sb.scrollLeft, top: sb.scrollTop};
47875     },
47876
47877     stripeRows : function(startRow){
47878         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47879             return;
47880         }
47881         startRow = startRow || 0;
47882         var rows = this.getBodyTable().rows;
47883         var lrows = this.getLockedTable().rows;
47884         var cls = ' x-grid-row-alt ';
47885         for(var i = startRow, len = rows.length; i < len; i++){
47886             var row = rows[i], lrow = lrows[i];
47887             var isAlt = ((i+1) % 2 == 0);
47888             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47889             if(isAlt == hasAlt){
47890                 continue;
47891             }
47892             if(isAlt){
47893                 row.className += " x-grid-row-alt";
47894             }else{
47895                 row.className = row.className.replace("x-grid-row-alt", "");
47896             }
47897             if(lrow){
47898                 lrow.className = row.className;
47899             }
47900         }
47901     },
47902
47903     restoreScroll : function(state){
47904         var sb = this.scroller.dom;
47905         sb.scrollLeft = state.left;
47906         sb.scrollTop = state.top;
47907         this.syncScroll();
47908     },
47909
47910     syncScroll : function(){
47911         var sb = this.scroller.dom;
47912         var sh = this.mainHd.dom;
47913         var bs = this.mainBody.dom;
47914         var lv = this.lockedBody.dom;
47915         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47916         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47917     },
47918
47919     handleScroll : function(e){
47920         this.syncScroll();
47921         var sb = this.scroller.dom;
47922         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47923         e.stopEvent();
47924     },
47925
47926     handleWheel : function(e){
47927         var d = e.getWheelDelta();
47928         this.scroller.dom.scrollTop -= d*22;
47929         // set this here to prevent jumpy scrolling on large tables
47930         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47931         e.stopEvent();
47932     },
47933
47934     renderRows : function(startRow, endRow){
47935         // pull in all the crap needed to render rows
47936         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47937         var colCount = cm.getColumnCount();
47938
47939         if(ds.getCount() < 1){
47940             return ["", ""];
47941         }
47942
47943         // build a map for all the columns
47944         var cs = [];
47945         for(var i = 0; i < colCount; i++){
47946             var name = cm.getDataIndex(i);
47947             cs[i] = {
47948                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47949                 renderer : cm.getRenderer(i),
47950                 id : cm.getColumnId(i),
47951                 locked : cm.isLocked(i)
47952             };
47953         }
47954
47955         startRow = startRow || 0;
47956         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47957
47958         // records to render
47959         var rs = ds.getRange(startRow, endRow);
47960
47961         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47962     },
47963
47964     // As much as I hate to duplicate code, this was branched because FireFox really hates
47965     // [].join("") on strings. The performance difference was substantial enough to
47966     // branch this function
47967     doRender : Roo.isGecko ?
47968             function(cs, rs, ds, startRow, colCount, stripe){
47969                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47970                 // buffers
47971                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47972                 for(var j = 0, len = rs.length; j < len; j++){
47973                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47974                     for(var i = 0; i < colCount; i++){
47975                         c = cs[i];
47976                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47977                         p.id = c.id;
47978                         p.css = p.attr = "";
47979                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47980                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47981                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47982                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47983                         }
47984                         var markup = ct.apply(p);
47985                         if(!c.locked){
47986                             cb+= markup;
47987                         }else{
47988                             lcb+= markup;
47989                         }
47990                     }
47991                     var alt = [];
47992                     if(stripe && ((rowIndex+1) % 2 == 0)){
47993                         alt[0] = "x-grid-row-alt";
47994                     }
47995                     if(r.dirty){
47996                         alt[1] = " x-grid-dirty-row";
47997                     }
47998                     rp.cells = lcb;
47999                     if(this.getRowClass){
48000                         alt[2] = this.getRowClass(r, rowIndex);
48001                     }
48002                     rp.alt = alt.join(" ");
48003                     lbuf+= rt.apply(rp);
48004                     rp.cells = cb;
48005                     buf+=  rt.apply(rp);
48006                 }
48007                 return [lbuf, buf];
48008             } :
48009             function(cs, rs, ds, startRow, colCount, stripe){
48010                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48011                 // buffers
48012                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48013                 for(var j = 0, len = rs.length; j < len; j++){
48014                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48015                     for(var i = 0; i < colCount; i++){
48016                         c = cs[i];
48017                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48018                         p.id = c.id;
48019                         p.css = p.attr = "";
48020                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48021                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48022                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48023                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48024                         }
48025                         var markup = ct.apply(p);
48026                         if(!c.locked){
48027                             cb[cb.length] = markup;
48028                         }else{
48029                             lcb[lcb.length] = markup;
48030                         }
48031                     }
48032                     var alt = [];
48033                     if(stripe && ((rowIndex+1) % 2 == 0)){
48034                         alt[0] = "x-grid-row-alt";
48035                     }
48036                     if(r.dirty){
48037                         alt[1] = " x-grid-dirty-row";
48038                     }
48039                     rp.cells = lcb;
48040                     if(this.getRowClass){
48041                         alt[2] = this.getRowClass(r, rowIndex);
48042                     }
48043                     rp.alt = alt.join(" ");
48044                     rp.cells = lcb.join("");
48045                     lbuf[lbuf.length] = rt.apply(rp);
48046                     rp.cells = cb.join("");
48047                     buf[buf.length] =  rt.apply(rp);
48048                 }
48049                 return [lbuf.join(""), buf.join("")];
48050             },
48051
48052     renderBody : function(){
48053         var markup = this.renderRows();
48054         var bt = this.templates.body;
48055         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48056     },
48057
48058     /**
48059      * Refreshes the grid
48060      * @param {Boolean} headersToo
48061      */
48062     refresh : function(headersToo){
48063         this.fireEvent("beforerefresh", this);
48064         this.grid.stopEditing();
48065         var result = this.renderBody();
48066         this.lockedBody.update(result[0]);
48067         this.mainBody.update(result[1]);
48068         if(headersToo === true){
48069             this.updateHeaders();
48070             this.updateColumns();
48071             this.updateSplitters();
48072             this.updateHeaderSortState();
48073         }
48074         this.syncRowHeights();
48075         this.layout();
48076         this.fireEvent("refresh", this);
48077     },
48078
48079     handleColumnMove : function(cm, oldIndex, newIndex){
48080         this.indexMap = null;
48081         var s = this.getScrollState();
48082         this.refresh(true);
48083         this.restoreScroll(s);
48084         this.afterMove(newIndex);
48085     },
48086
48087     afterMove : function(colIndex){
48088         if(this.enableMoveAnim && Roo.enableFx){
48089             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48090         }
48091     },
48092
48093     updateCell : function(dm, rowIndex, dataIndex){
48094         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48095         if(typeof colIndex == "undefined"){ // not present in grid
48096             return;
48097         }
48098         var cm = this.grid.colModel;
48099         var cell = this.getCell(rowIndex, colIndex);
48100         var cellText = this.getCellText(rowIndex, colIndex);
48101
48102         var p = {
48103             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48104             id : cm.getColumnId(colIndex),
48105             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48106         };
48107         var renderer = cm.getRenderer(colIndex);
48108         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48109         if(typeof val == "undefined" || val === "") val = "&#160;";
48110         cellText.innerHTML = val;
48111         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48112         this.syncRowHeights(rowIndex, rowIndex);
48113     },
48114
48115     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48116         var maxWidth = 0;
48117         if(this.grid.autoSizeHeaders){
48118             var h = this.getHeaderCellMeasure(colIndex);
48119             maxWidth = Math.max(maxWidth, h.scrollWidth);
48120         }
48121         var tb, index;
48122         if(this.cm.isLocked(colIndex)){
48123             tb = this.getLockedTable();
48124             index = colIndex;
48125         }else{
48126             tb = this.getBodyTable();
48127             index = colIndex - this.cm.getLockedCount();
48128         }
48129         if(tb && tb.rows){
48130             var rows = tb.rows;
48131             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48132             for(var i = 0; i < stopIndex; i++){
48133                 var cell = rows[i].childNodes[index].firstChild;
48134                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48135             }
48136         }
48137         return maxWidth + /*margin for error in IE*/ 5;
48138     },
48139     /**
48140      * Autofit a column to its content.
48141      * @param {Number} colIndex
48142      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48143      */
48144      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48145          if(this.cm.isHidden(colIndex)){
48146              return; // can't calc a hidden column
48147          }
48148         if(forceMinSize){
48149             var cid = this.cm.getColumnId(colIndex);
48150             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48151            if(this.grid.autoSizeHeaders){
48152                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48153            }
48154         }
48155         var newWidth = this.calcColumnWidth(colIndex);
48156         this.cm.setColumnWidth(colIndex,
48157             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48158         if(!suppressEvent){
48159             this.grid.fireEvent("columnresize", colIndex, newWidth);
48160         }
48161     },
48162
48163     /**
48164      * Autofits all columns to their content and then expands to fit any extra space in the grid
48165      */
48166      autoSizeColumns : function(){
48167         var cm = this.grid.colModel;
48168         var colCount = cm.getColumnCount();
48169         for(var i = 0; i < colCount; i++){
48170             this.autoSizeColumn(i, true, true);
48171         }
48172         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48173             this.fitColumns();
48174         }else{
48175             this.updateColumns();
48176             this.layout();
48177         }
48178     },
48179
48180     /**
48181      * Autofits all columns to the grid's width proportionate with their current size
48182      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48183      */
48184     fitColumns : function(reserveScrollSpace){
48185         var cm = this.grid.colModel;
48186         var colCount = cm.getColumnCount();
48187         var cols = [];
48188         var width = 0;
48189         var i, w;
48190         for (i = 0; i < colCount; i++){
48191             if(!cm.isHidden(i) && !cm.isFixed(i)){
48192                 w = cm.getColumnWidth(i);
48193                 cols.push(i);
48194                 cols.push(w);
48195                 width += w;
48196             }
48197         }
48198         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48199         if(reserveScrollSpace){
48200             avail -= 17;
48201         }
48202         var frac = (avail - cm.getTotalWidth())/width;
48203         while (cols.length){
48204             w = cols.pop();
48205             i = cols.pop();
48206             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48207         }
48208         this.updateColumns();
48209         this.layout();
48210     },
48211
48212     onRowSelect : function(rowIndex){
48213         var row = this.getRowComposite(rowIndex);
48214         row.addClass("x-grid-row-selected");
48215     },
48216
48217     onRowDeselect : function(rowIndex){
48218         var row = this.getRowComposite(rowIndex);
48219         row.removeClass("x-grid-row-selected");
48220     },
48221
48222     onCellSelect : function(row, col){
48223         var cell = this.getCell(row, col);
48224         if(cell){
48225             Roo.fly(cell).addClass("x-grid-cell-selected");
48226         }
48227     },
48228
48229     onCellDeselect : function(row, col){
48230         var cell = this.getCell(row, col);
48231         if(cell){
48232             Roo.fly(cell).removeClass("x-grid-cell-selected");
48233         }
48234     },
48235
48236     updateHeaderSortState : function(){
48237         var state = this.ds.getSortState();
48238         if(!state){
48239             return;
48240         }
48241         this.sortState = state;
48242         var sortColumn = this.cm.findColumnIndex(state.field);
48243         if(sortColumn != -1){
48244             var sortDir = state.direction;
48245             var sc = this.sortClasses;
48246             var hds = this.el.select(this.headerSelector).removeClass(sc);
48247             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48248         }
48249     },
48250
48251     handleHeaderClick : function(g, index){
48252         if(this.headersDisabled){
48253             return;
48254         }
48255         var dm = g.dataSource, cm = g.colModel;
48256             if(!cm.isSortable(index)){
48257             return;
48258         }
48259             g.stopEditing();
48260         dm.sort(cm.getDataIndex(index));
48261     },
48262
48263
48264     destroy : function(){
48265         if(this.colMenu){
48266             this.colMenu.removeAll();
48267             Roo.menu.MenuMgr.unregister(this.colMenu);
48268             this.colMenu.getEl().remove();
48269             delete this.colMenu;
48270         }
48271         if(this.hmenu){
48272             this.hmenu.removeAll();
48273             Roo.menu.MenuMgr.unregister(this.hmenu);
48274             this.hmenu.getEl().remove();
48275             delete this.hmenu;
48276         }
48277         if(this.grid.enableColumnMove){
48278             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48279             if(dds){
48280                 for(var dd in dds){
48281                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48282                         var elid = dds[dd].dragElId;
48283                         dds[dd].unreg();
48284                         Roo.get(elid).remove();
48285                     } else if(dds[dd].config.isTarget){
48286                         dds[dd].proxyTop.remove();
48287                         dds[dd].proxyBottom.remove();
48288                         dds[dd].unreg();
48289                     }
48290                     if(Roo.dd.DDM.locationCache[dd]){
48291                         delete Roo.dd.DDM.locationCache[dd];
48292                     }
48293                 }
48294                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48295             }
48296         }
48297         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48298         this.bind(null, null);
48299         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48300     },
48301
48302     handleLockChange : function(){
48303         this.refresh(true);
48304     },
48305
48306     onDenyColumnLock : function(){
48307
48308     },
48309
48310     onDenyColumnHide : function(){
48311
48312     },
48313
48314     handleHdMenuClick : function(item){
48315         var index = this.hdCtxIndex;
48316         var cm = this.cm, ds = this.ds;
48317         switch(item.id){
48318             case "asc":
48319                 ds.sort(cm.getDataIndex(index), "ASC");
48320                 break;
48321             case "desc":
48322                 ds.sort(cm.getDataIndex(index), "DESC");
48323                 break;
48324             case "lock":
48325                 var lc = cm.getLockedCount();
48326                 if(cm.getColumnCount(true) <= lc+1){
48327                     this.onDenyColumnLock();
48328                     return;
48329                 }
48330                 if(lc != index){
48331                     cm.setLocked(index, true, true);
48332                     cm.moveColumn(index, lc);
48333                     this.grid.fireEvent("columnmove", index, lc);
48334                 }else{
48335                     cm.setLocked(index, true);
48336                 }
48337             break;
48338             case "unlock":
48339                 var lc = cm.getLockedCount();
48340                 if((lc-1) != index){
48341                     cm.setLocked(index, false, true);
48342                     cm.moveColumn(index, lc-1);
48343                     this.grid.fireEvent("columnmove", index, lc-1);
48344                 }else{
48345                     cm.setLocked(index, false);
48346                 }
48347             break;
48348             default:
48349                 index = cm.getIndexById(item.id.substr(4));
48350                 if(index != -1){
48351                     if(item.checked && cm.getColumnCount(true) <= 1){
48352                         this.onDenyColumnHide();
48353                         return false;
48354                     }
48355                     cm.setHidden(index, item.checked);
48356                 }
48357         }
48358         return true;
48359     },
48360
48361     beforeColMenuShow : function(){
48362         var cm = this.cm,  colCount = cm.getColumnCount();
48363         this.colMenu.removeAll();
48364         for(var i = 0; i < colCount; i++){
48365             this.colMenu.add(new Roo.menu.CheckItem({
48366                 id: "col-"+cm.getColumnId(i),
48367                 text: cm.getColumnHeader(i),
48368                 checked: !cm.isHidden(i),
48369                 hideOnClick:false
48370             }));
48371         }
48372     },
48373
48374     handleHdCtx : function(g, index, e){
48375         e.stopEvent();
48376         var hd = this.getHeaderCell(index);
48377         this.hdCtxIndex = index;
48378         var ms = this.hmenu.items, cm = this.cm;
48379         ms.get("asc").setDisabled(!cm.isSortable(index));
48380         ms.get("desc").setDisabled(!cm.isSortable(index));
48381         if(this.grid.enableColLock !== false){
48382             ms.get("lock").setDisabled(cm.isLocked(index));
48383             ms.get("unlock").setDisabled(!cm.isLocked(index));
48384         }
48385         this.hmenu.show(hd, "tl-bl");
48386     },
48387
48388     handleHdOver : function(e){
48389         var hd = this.findHeaderCell(e.getTarget());
48390         if(hd && !this.headersDisabled){
48391             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48392                this.fly(hd).addClass("x-grid-hd-over");
48393             }
48394         }
48395     },
48396
48397     handleHdOut : function(e){
48398         var hd = this.findHeaderCell(e.getTarget());
48399         if(hd){
48400             this.fly(hd).removeClass("x-grid-hd-over");
48401         }
48402     },
48403
48404     handleSplitDblClick : function(e, t){
48405         var i = this.getCellIndex(t);
48406         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48407             this.autoSizeColumn(i, true);
48408             this.layout();
48409         }
48410     },
48411
48412     render : function(){
48413
48414         var cm = this.cm;
48415         var colCount = cm.getColumnCount();
48416
48417         if(this.grid.monitorWindowResize === true){
48418             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48419         }
48420         var header = this.renderHeaders();
48421         var body = this.templates.body.apply({rows:""});
48422         var html = this.templates.master.apply({
48423             lockedBody: body,
48424             body: body,
48425             lockedHeader: header[0],
48426             header: header[1]
48427         });
48428
48429         //this.updateColumns();
48430
48431         this.grid.getGridEl().dom.innerHTML = html;
48432
48433         this.initElements();
48434         
48435         // a kludge to fix the random scolling effect in webkit
48436         this.el.on("scroll", function() {
48437             this.el.dom.scrollTop=0; // hopefully not recursive..
48438         },this);
48439
48440         this.scroller.on("scroll", this.handleScroll, this);
48441         this.lockedBody.on("mousewheel", this.handleWheel, this);
48442         this.mainBody.on("mousewheel", this.handleWheel, this);
48443
48444         this.mainHd.on("mouseover", this.handleHdOver, this);
48445         this.mainHd.on("mouseout", this.handleHdOut, this);
48446         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48447                 {delegate: "."+this.splitClass});
48448
48449         this.lockedHd.on("mouseover", this.handleHdOver, this);
48450         this.lockedHd.on("mouseout", this.handleHdOut, this);
48451         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48452                 {delegate: "."+this.splitClass});
48453
48454         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48455             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48456         }
48457
48458         this.updateSplitters();
48459
48460         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48461             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48462             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48463         }
48464
48465         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48466             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48467             this.hmenu.add(
48468                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48469                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48470             );
48471             if(this.grid.enableColLock !== false){
48472                 this.hmenu.add('-',
48473                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48474                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48475                 );
48476             }
48477             if(this.grid.enableColumnHide !== false){
48478
48479                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48480                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48481                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48482
48483                 this.hmenu.add('-',
48484                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48485                 );
48486             }
48487             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48488
48489             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48490         }
48491
48492         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48493             this.dd = new Roo.grid.GridDragZone(this.grid, {
48494                 ddGroup : this.grid.ddGroup || 'GridDD'
48495             });
48496         }
48497
48498         /*
48499         for(var i = 0; i < colCount; i++){
48500             if(cm.isHidden(i)){
48501                 this.hideColumn(i);
48502             }
48503             if(cm.config[i].align){
48504                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48505                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48506             }
48507         }*/
48508         
48509         this.updateHeaderSortState();
48510
48511         this.beforeInitialResize();
48512         this.layout(true);
48513
48514         // two part rendering gives faster view to the user
48515         this.renderPhase2.defer(1, this);
48516     },
48517
48518     renderPhase2 : function(){
48519         // render the rows now
48520         this.refresh();
48521         if(this.grid.autoSizeColumns){
48522             this.autoSizeColumns();
48523         }
48524     },
48525
48526     beforeInitialResize : function(){
48527
48528     },
48529
48530     onColumnSplitterMoved : function(i, w){
48531         this.userResized = true;
48532         var cm = this.grid.colModel;
48533         cm.setColumnWidth(i, w, true);
48534         var cid = cm.getColumnId(i);
48535         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48536         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48537         this.updateSplitters();
48538         this.layout();
48539         this.grid.fireEvent("columnresize", i, w);
48540     },
48541
48542     syncRowHeights : function(startIndex, endIndex){
48543         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48544             startIndex = startIndex || 0;
48545             var mrows = this.getBodyTable().rows;
48546             var lrows = this.getLockedTable().rows;
48547             var len = mrows.length-1;
48548             endIndex = Math.min(endIndex || len, len);
48549             for(var i = startIndex; i <= endIndex; i++){
48550                 var m = mrows[i], l = lrows[i];
48551                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48552                 m.style.height = l.style.height = h + "px";
48553             }
48554         }
48555     },
48556
48557     layout : function(initialRender, is2ndPass){
48558         var g = this.grid;
48559         var auto = g.autoHeight;
48560         var scrollOffset = 16;
48561         var c = g.getGridEl(), cm = this.cm,
48562                 expandCol = g.autoExpandColumn,
48563                 gv = this;
48564         //c.beginMeasure();
48565
48566         if(!c.dom.offsetWidth){ // display:none?
48567             if(initialRender){
48568                 this.lockedWrap.show();
48569                 this.mainWrap.show();
48570             }
48571             return;
48572         }
48573
48574         var hasLock = this.cm.isLocked(0);
48575
48576         var tbh = this.headerPanel.getHeight();
48577         var bbh = this.footerPanel.getHeight();
48578
48579         if(auto){
48580             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48581             var newHeight = ch + c.getBorderWidth("tb");
48582             if(g.maxHeight){
48583                 newHeight = Math.min(g.maxHeight, newHeight);
48584             }
48585             c.setHeight(newHeight);
48586         }
48587
48588         if(g.autoWidth){
48589             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48590         }
48591
48592         var s = this.scroller;
48593
48594         var csize = c.getSize(true);
48595
48596         this.el.setSize(csize.width, csize.height);
48597
48598         this.headerPanel.setWidth(csize.width);
48599         this.footerPanel.setWidth(csize.width);
48600
48601         var hdHeight = this.mainHd.getHeight();
48602         var vw = csize.width;
48603         var vh = csize.height - (tbh + bbh);
48604
48605         s.setSize(vw, vh);
48606
48607         var bt = this.getBodyTable();
48608         var ltWidth = hasLock ?
48609                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48610
48611         var scrollHeight = bt.offsetHeight;
48612         var scrollWidth = ltWidth + bt.offsetWidth;
48613         var vscroll = false, hscroll = false;
48614
48615         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48616
48617         var lw = this.lockedWrap, mw = this.mainWrap;
48618         var lb = this.lockedBody, mb = this.mainBody;
48619
48620         setTimeout(function(){
48621             var t = s.dom.offsetTop;
48622             var w = s.dom.clientWidth,
48623                 h = s.dom.clientHeight;
48624
48625             lw.setTop(t);
48626             lw.setSize(ltWidth, h);
48627
48628             mw.setLeftTop(ltWidth, t);
48629             mw.setSize(w-ltWidth, h);
48630
48631             lb.setHeight(h-hdHeight);
48632             mb.setHeight(h-hdHeight);
48633
48634             if(is2ndPass !== true && !gv.userResized && expandCol){
48635                 // high speed resize without full column calculation
48636                 
48637                 var ci = cm.getIndexById(expandCol);
48638                 if (ci < 0) {
48639                     ci = cm.findColumnIndex(expandCol);
48640                 }
48641                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48642                 var expandId = cm.getColumnId(ci);
48643                 var  tw = cm.getTotalWidth(false);
48644                 var currentWidth = cm.getColumnWidth(ci);
48645                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48646                 if(currentWidth != cw){
48647                     cm.setColumnWidth(ci, cw, true);
48648                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48649                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48650                     gv.updateSplitters();
48651                     gv.layout(false, true);
48652                 }
48653             }
48654
48655             if(initialRender){
48656                 lw.show();
48657                 mw.show();
48658             }
48659             //c.endMeasure();
48660         }, 10);
48661     },
48662
48663     onWindowResize : function(){
48664         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48665             return;
48666         }
48667         this.layout();
48668     },
48669
48670     appendFooter : function(parentEl){
48671         return null;
48672     },
48673
48674     sortAscText : "Sort Ascending",
48675     sortDescText : "Sort Descending",
48676     lockText : "Lock Column",
48677     unlockText : "Unlock Column",
48678     columnsText : "Columns"
48679 });
48680
48681
48682 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48683     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48684     this.proxy.el.addClass('x-grid3-col-dd');
48685 };
48686
48687 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48688     handleMouseDown : function(e){
48689
48690     },
48691
48692     callHandleMouseDown : function(e){
48693         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48694     }
48695 });
48696 /*
48697  * Based on:
48698  * Ext JS Library 1.1.1
48699  * Copyright(c) 2006-2007, Ext JS, LLC.
48700  *
48701  * Originally Released Under LGPL - original licence link has changed is not relivant.
48702  *
48703  * Fork - LGPL
48704  * <script type="text/javascript">
48705  */
48706  
48707 // private
48708 // This is a support class used internally by the Grid components
48709 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48710     this.grid = grid;
48711     this.view = grid.getView();
48712     this.proxy = this.view.resizeProxy;
48713     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48714         "gridSplitters" + this.grid.getGridEl().id, {
48715         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48716     });
48717     this.setHandleElId(Roo.id(hd));
48718     this.setOuterHandleElId(Roo.id(hd2));
48719     this.scroll = false;
48720 };
48721 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48722     fly: Roo.Element.fly,
48723
48724     b4StartDrag : function(x, y){
48725         this.view.headersDisabled = true;
48726         this.proxy.setHeight(this.view.mainWrap.getHeight());
48727         var w = this.cm.getColumnWidth(this.cellIndex);
48728         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48729         this.resetConstraints();
48730         this.setXConstraint(minw, 1000);
48731         this.setYConstraint(0, 0);
48732         this.minX = x - minw;
48733         this.maxX = x + 1000;
48734         this.startPos = x;
48735         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48736     },
48737
48738
48739     handleMouseDown : function(e){
48740         ev = Roo.EventObject.setEvent(e);
48741         var t = this.fly(ev.getTarget());
48742         if(t.hasClass("x-grid-split")){
48743             this.cellIndex = this.view.getCellIndex(t.dom);
48744             this.split = t.dom;
48745             this.cm = this.grid.colModel;
48746             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48747                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48748             }
48749         }
48750     },
48751
48752     endDrag : function(e){
48753         this.view.headersDisabled = false;
48754         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48755         var diff = endX - this.startPos;
48756         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48757     },
48758
48759     autoOffset : function(){
48760         this.setDelta(0,0);
48761     }
48762 });/*
48763  * Based on:
48764  * Ext JS Library 1.1.1
48765  * Copyright(c) 2006-2007, Ext JS, LLC.
48766  *
48767  * Originally Released Under LGPL - original licence link has changed is not relivant.
48768  *
48769  * Fork - LGPL
48770  * <script type="text/javascript">
48771  */
48772  
48773 // private
48774 // This is a support class used internally by the Grid components
48775 Roo.grid.GridDragZone = function(grid, config){
48776     this.view = grid.getView();
48777     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48778     if(this.view.lockedBody){
48779         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48780         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48781     }
48782     this.scroll = false;
48783     this.grid = grid;
48784     this.ddel = document.createElement('div');
48785     this.ddel.className = 'x-grid-dd-wrap';
48786 };
48787
48788 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48789     ddGroup : "GridDD",
48790
48791     getDragData : function(e){
48792         var t = Roo.lib.Event.getTarget(e);
48793         var rowIndex = this.view.findRowIndex(t);
48794         if(rowIndex !== false){
48795             var sm = this.grid.selModel;
48796             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48797               //  sm.mouseDown(e, t);
48798             //}
48799             if (e.hasModifier()){
48800                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48801             }
48802             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48803         }
48804         return false;
48805     },
48806
48807     onInitDrag : function(e){
48808         var data = this.dragData;
48809         this.ddel.innerHTML = this.grid.getDragDropText();
48810         this.proxy.update(this.ddel);
48811         // fire start drag?
48812     },
48813
48814     afterRepair : function(){
48815         this.dragging = false;
48816     },
48817
48818     getRepairXY : function(e, data){
48819         return false;
48820     },
48821
48822     onEndDrag : function(data, e){
48823         // fire end drag?
48824     },
48825
48826     onValidDrop : function(dd, e, id){
48827         // fire drag drop?
48828         this.hideProxy();
48829     },
48830
48831     beforeInvalidDrop : function(e, id){
48832
48833     }
48834 });/*
48835  * Based on:
48836  * Ext JS Library 1.1.1
48837  * Copyright(c) 2006-2007, Ext JS, LLC.
48838  *
48839  * Originally Released Under LGPL - original licence link has changed is not relivant.
48840  *
48841  * Fork - LGPL
48842  * <script type="text/javascript">
48843  */
48844  
48845
48846 /**
48847  * @class Roo.grid.ColumnModel
48848  * @extends Roo.util.Observable
48849  * This is the default implementation of a ColumnModel used by the Grid. It defines
48850  * the columns in the grid.
48851  * <br>Usage:<br>
48852  <pre><code>
48853  var colModel = new Roo.grid.ColumnModel([
48854         {header: "Ticker", width: 60, sortable: true, locked: true},
48855         {header: "Company Name", width: 150, sortable: true},
48856         {header: "Market Cap.", width: 100, sortable: true},
48857         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48858         {header: "Employees", width: 100, sortable: true, resizable: false}
48859  ]);
48860  </code></pre>
48861  * <p>
48862  
48863  * The config options listed for this class are options which may appear in each
48864  * individual column definition.
48865  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48866  * @constructor
48867  * @param {Object} config An Array of column config objects. See this class's
48868  * config objects for details.
48869 */
48870 Roo.grid.ColumnModel = function(config){
48871         /**
48872      * The config passed into the constructor
48873      */
48874     this.config = config;
48875     this.lookup = {};
48876
48877     // if no id, create one
48878     // if the column does not have a dataIndex mapping,
48879     // map it to the order it is in the config
48880     for(var i = 0, len = config.length; i < len; i++){
48881         var c = config[i];
48882         if(typeof c.dataIndex == "undefined"){
48883             c.dataIndex = i;
48884         }
48885         if(typeof c.renderer == "string"){
48886             c.renderer = Roo.util.Format[c.renderer];
48887         }
48888         if(typeof c.id == "undefined"){
48889             c.id = Roo.id();
48890         }
48891         if(c.editor && c.editor.xtype){
48892             c.editor  = Roo.factory(c.editor, Roo.grid);
48893         }
48894         if(c.editor && c.editor.isFormField){
48895             c.editor = new Roo.grid.GridEditor(c.editor);
48896         }
48897         this.lookup[c.id] = c;
48898     }
48899
48900     /**
48901      * The width of columns which have no width specified (defaults to 100)
48902      * @type Number
48903      */
48904     this.defaultWidth = 100;
48905
48906     /**
48907      * Default sortable of columns which have no sortable specified (defaults to false)
48908      * @type Boolean
48909      */
48910     this.defaultSortable = false;
48911
48912     this.addEvents({
48913         /**
48914              * @event widthchange
48915              * Fires when the width of a column changes.
48916              * @param {ColumnModel} this
48917              * @param {Number} columnIndex The column index
48918              * @param {Number} newWidth The new width
48919              */
48920             "widthchange": true,
48921         /**
48922              * @event headerchange
48923              * Fires when the text of a header changes.
48924              * @param {ColumnModel} this
48925              * @param {Number} columnIndex The column index
48926              * @param {Number} newText The new header text
48927              */
48928             "headerchange": true,
48929         /**
48930              * @event hiddenchange
48931              * Fires when a column is hidden or "unhidden".
48932              * @param {ColumnModel} this
48933              * @param {Number} columnIndex The column index
48934              * @param {Boolean} hidden true if hidden, false otherwise
48935              */
48936             "hiddenchange": true,
48937             /**
48938          * @event columnmoved
48939          * Fires when a column is moved.
48940          * @param {ColumnModel} this
48941          * @param {Number} oldIndex
48942          * @param {Number} newIndex
48943          */
48944         "columnmoved" : true,
48945         /**
48946          * @event columlockchange
48947          * Fires when a column's locked state is changed
48948          * @param {ColumnModel} this
48949          * @param {Number} colIndex
48950          * @param {Boolean} locked true if locked
48951          */
48952         "columnlockchange" : true
48953     });
48954     Roo.grid.ColumnModel.superclass.constructor.call(this);
48955 };
48956 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48957     /**
48958      * @cfg {String} header The header text to display in the Grid view.
48959      */
48960     /**
48961      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48962      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48963      * specified, the column's index is used as an index into the Record's data Array.
48964      */
48965     /**
48966      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48967      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48968      */
48969     /**
48970      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48971      * Defaults to the value of the {@link #defaultSortable} property.
48972      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48973      */
48974     /**
48975      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48976      */
48977     /**
48978      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48979      */
48980     /**
48981      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48982      */
48983     /**
48984      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48985      */
48986     /**
48987      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48988      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48989      * default renderer uses the raw data value.
48990      */
48991        /**
48992      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48993      */
48994     /**
48995      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48996      */
48997
48998     /**
48999      * Returns the id of the column at the specified index.
49000      * @param {Number} index The column index
49001      * @return {String} the id
49002      */
49003     getColumnId : function(index){
49004         return this.config[index].id;
49005     },
49006
49007     /**
49008      * Returns the column for a specified id.
49009      * @param {String} id The column id
49010      * @return {Object} the column
49011      */
49012     getColumnById : function(id){
49013         return this.lookup[id];
49014     },
49015
49016     
49017     /**
49018      * Returns the column for a specified dataIndex.
49019      * @param {String} dataIndex The column dataIndex
49020      * @return {Object|Boolean} the column or false if not found
49021      */
49022     getColumnByDataIndex: function(dataIndex){
49023         var index = this.findColumnIndex(dataIndex);
49024         return index > -1 ? this.config[index] : false;
49025     },
49026     
49027     /**
49028      * Returns the index for a specified column id.
49029      * @param {String} id The column id
49030      * @return {Number} the index, or -1 if not found
49031      */
49032     getIndexById : function(id){
49033         for(var i = 0, len = this.config.length; i < len; i++){
49034             if(this.config[i].id == id){
49035                 return i;
49036             }
49037         }
49038         return -1;
49039     },
49040     
49041     /**
49042      * Returns the index for a specified column dataIndex.
49043      * @param {String} dataIndex The column dataIndex
49044      * @return {Number} the index, or -1 if not found
49045      */
49046     
49047     findColumnIndex : function(dataIndex){
49048         for(var i = 0, len = this.config.length; i < len; i++){
49049             if(this.config[i].dataIndex == dataIndex){
49050                 return i;
49051             }
49052         }
49053         return -1;
49054     },
49055     
49056     
49057     moveColumn : function(oldIndex, newIndex){
49058         var c = this.config[oldIndex];
49059         this.config.splice(oldIndex, 1);
49060         this.config.splice(newIndex, 0, c);
49061         this.dataMap = null;
49062         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49063     },
49064
49065     isLocked : function(colIndex){
49066         return this.config[colIndex].locked === true;
49067     },
49068
49069     setLocked : function(colIndex, value, suppressEvent){
49070         if(this.isLocked(colIndex) == value){
49071             return;
49072         }
49073         this.config[colIndex].locked = value;
49074         if(!suppressEvent){
49075             this.fireEvent("columnlockchange", this, colIndex, value);
49076         }
49077     },
49078
49079     getTotalLockedWidth : function(){
49080         var totalWidth = 0;
49081         for(var i = 0; i < this.config.length; i++){
49082             if(this.isLocked(i) && !this.isHidden(i)){
49083                 this.totalWidth += this.getColumnWidth(i);
49084             }
49085         }
49086         return totalWidth;
49087     },
49088
49089     getLockedCount : function(){
49090         for(var i = 0, len = this.config.length; i < len; i++){
49091             if(!this.isLocked(i)){
49092                 return i;
49093             }
49094         }
49095     },
49096
49097     /**
49098      * Returns the number of columns.
49099      * @return {Number}
49100      */
49101     getColumnCount : function(visibleOnly){
49102         if(visibleOnly === true){
49103             var c = 0;
49104             for(var i = 0, len = this.config.length; i < len; i++){
49105                 if(!this.isHidden(i)){
49106                     c++;
49107                 }
49108             }
49109             return c;
49110         }
49111         return this.config.length;
49112     },
49113
49114     /**
49115      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49116      * @param {Function} fn
49117      * @param {Object} scope (optional)
49118      * @return {Array} result
49119      */
49120     getColumnsBy : function(fn, scope){
49121         var r = [];
49122         for(var i = 0, len = this.config.length; i < len; i++){
49123             var c = this.config[i];
49124             if(fn.call(scope||this, c, i) === true){
49125                 r[r.length] = c;
49126             }
49127         }
49128         return r;
49129     },
49130
49131     /**
49132      * Returns true if the specified column is sortable.
49133      * @param {Number} col The column index
49134      * @return {Boolean}
49135      */
49136     isSortable : function(col){
49137         if(typeof this.config[col].sortable == "undefined"){
49138             return this.defaultSortable;
49139         }
49140         return this.config[col].sortable;
49141     },
49142
49143     /**
49144      * Returns the rendering (formatting) function defined for the column.
49145      * @param {Number} col The column index.
49146      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49147      */
49148     getRenderer : function(col){
49149         if(!this.config[col].renderer){
49150             return Roo.grid.ColumnModel.defaultRenderer;
49151         }
49152         return this.config[col].renderer;
49153     },
49154
49155     /**
49156      * Sets the rendering (formatting) function for a column.
49157      * @param {Number} col The column index
49158      * @param {Function} fn The function to use to process the cell's raw data
49159      * to return HTML markup for the grid view. The render function is called with
49160      * the following parameters:<ul>
49161      * <li>Data value.</li>
49162      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49163      * <li>css A CSS style string to apply to the table cell.</li>
49164      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49165      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49166      * <li>Row index</li>
49167      * <li>Column index</li>
49168      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49169      */
49170     setRenderer : function(col, fn){
49171         this.config[col].renderer = fn;
49172     },
49173
49174     /**
49175      * Returns the width for the specified column.
49176      * @param {Number} col The column index
49177      * @return {Number}
49178      */
49179     getColumnWidth : function(col){
49180         return this.config[col].width || this.defaultWidth;
49181     },
49182
49183     /**
49184      * Sets the width for a column.
49185      * @param {Number} col The column index
49186      * @param {Number} width The new width
49187      */
49188     setColumnWidth : function(col, width, suppressEvent){
49189         this.config[col].width = width;
49190         this.totalWidth = null;
49191         if(!suppressEvent){
49192              this.fireEvent("widthchange", this, col, width);
49193         }
49194     },
49195
49196     /**
49197      * Returns the total width of all columns.
49198      * @param {Boolean} includeHidden True to include hidden column widths
49199      * @return {Number}
49200      */
49201     getTotalWidth : function(includeHidden){
49202         if(!this.totalWidth){
49203             this.totalWidth = 0;
49204             for(var i = 0, len = this.config.length; i < len; i++){
49205                 if(includeHidden || !this.isHidden(i)){
49206                     this.totalWidth += this.getColumnWidth(i);
49207                 }
49208             }
49209         }
49210         return this.totalWidth;
49211     },
49212
49213     /**
49214      * Returns the header for the specified column.
49215      * @param {Number} col The column index
49216      * @return {String}
49217      */
49218     getColumnHeader : function(col){
49219         return this.config[col].header;
49220     },
49221
49222     /**
49223      * Sets the header for a column.
49224      * @param {Number} col The column index
49225      * @param {String} header The new header
49226      */
49227     setColumnHeader : function(col, header){
49228         this.config[col].header = header;
49229         this.fireEvent("headerchange", this, col, header);
49230     },
49231
49232     /**
49233      * Returns the tooltip for the specified column.
49234      * @param {Number} col The column index
49235      * @return {String}
49236      */
49237     getColumnTooltip : function(col){
49238             return this.config[col].tooltip;
49239     },
49240     /**
49241      * Sets the tooltip for a column.
49242      * @param {Number} col The column index
49243      * @param {String} tooltip The new tooltip
49244      */
49245     setColumnTooltip : function(col, tooltip){
49246             this.config[col].tooltip = tooltip;
49247     },
49248
49249     /**
49250      * Returns the dataIndex for the specified column.
49251      * @param {Number} col The column index
49252      * @return {Number}
49253      */
49254     getDataIndex : function(col){
49255         return this.config[col].dataIndex;
49256     },
49257
49258     /**
49259      * Sets the dataIndex for a column.
49260      * @param {Number} col The column index
49261      * @param {Number} dataIndex The new dataIndex
49262      */
49263     setDataIndex : function(col, dataIndex){
49264         this.config[col].dataIndex = dataIndex;
49265     },
49266
49267     
49268     
49269     /**
49270      * Returns true if the cell is editable.
49271      * @param {Number} colIndex The column index
49272      * @param {Number} rowIndex The row index
49273      * @return {Boolean}
49274      */
49275     isCellEditable : function(colIndex, rowIndex){
49276         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49277     },
49278
49279     /**
49280      * Returns the editor defined for the cell/column.
49281      * return false or null to disable editing.
49282      * @param {Number} colIndex The column index
49283      * @param {Number} rowIndex The row index
49284      * @return {Object}
49285      */
49286     getCellEditor : function(colIndex, rowIndex){
49287         return this.config[colIndex].editor;
49288     },
49289
49290     /**
49291      * Sets if a column is editable.
49292      * @param {Number} col The column index
49293      * @param {Boolean} editable True if the column is editable
49294      */
49295     setEditable : function(col, editable){
49296         this.config[col].editable = editable;
49297     },
49298
49299
49300     /**
49301      * Returns true if the column is hidden.
49302      * @param {Number} colIndex The column index
49303      * @return {Boolean}
49304      */
49305     isHidden : function(colIndex){
49306         return this.config[colIndex].hidden;
49307     },
49308
49309
49310     /**
49311      * Returns true if the column width cannot be changed
49312      */
49313     isFixed : function(colIndex){
49314         return this.config[colIndex].fixed;
49315     },
49316
49317     /**
49318      * Returns true if the column can be resized
49319      * @return {Boolean}
49320      */
49321     isResizable : function(colIndex){
49322         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49323     },
49324     /**
49325      * Sets if a column is hidden.
49326      * @param {Number} colIndex The column index
49327      * @param {Boolean} hidden True if the column is hidden
49328      */
49329     setHidden : function(colIndex, hidden){
49330         this.config[colIndex].hidden = hidden;
49331         this.totalWidth = null;
49332         this.fireEvent("hiddenchange", this, colIndex, hidden);
49333     },
49334
49335     /**
49336      * Sets the editor for a column.
49337      * @param {Number} col The column index
49338      * @param {Object} editor The editor object
49339      */
49340     setEditor : function(col, editor){
49341         this.config[col].editor = editor;
49342     }
49343 });
49344
49345 Roo.grid.ColumnModel.defaultRenderer = function(value){
49346         if(typeof value == "string" && value.length < 1){
49347             return "&#160;";
49348         }
49349         return value;
49350 };
49351
49352 // Alias for backwards compatibility
49353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49354 /*
49355  * Based on:
49356  * Ext JS Library 1.1.1
49357  * Copyright(c) 2006-2007, Ext JS, LLC.
49358  *
49359  * Originally Released Under LGPL - original licence link has changed is not relivant.
49360  *
49361  * Fork - LGPL
49362  * <script type="text/javascript">
49363  */
49364
49365 /**
49366  * @class Roo.grid.AbstractSelectionModel
49367  * @extends Roo.util.Observable
49368  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49369  * implemented by descendant classes.  This class should not be directly instantiated.
49370  * @constructor
49371  */
49372 Roo.grid.AbstractSelectionModel = function(){
49373     this.locked = false;
49374     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49375 };
49376
49377 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49378     /** @ignore Called by the grid automatically. Do not call directly. */
49379     init : function(grid){
49380         this.grid = grid;
49381         this.initEvents();
49382     },
49383
49384     /**
49385      * Locks the selections.
49386      */
49387     lock : function(){
49388         this.locked = true;
49389     },
49390
49391     /**
49392      * Unlocks the selections.
49393      */
49394     unlock : function(){
49395         this.locked = false;
49396     },
49397
49398     /**
49399      * Returns true if the selections are locked.
49400      * @return {Boolean}
49401      */
49402     isLocked : function(){
49403         return this.locked;
49404     }
49405 });/*
49406  * Based on:
49407  * Ext JS Library 1.1.1
49408  * Copyright(c) 2006-2007, Ext JS, LLC.
49409  *
49410  * Originally Released Under LGPL - original licence link has changed is not relivant.
49411  *
49412  * Fork - LGPL
49413  * <script type="text/javascript">
49414  */
49415 /**
49416  * @extends Roo.grid.AbstractSelectionModel
49417  * @class Roo.grid.RowSelectionModel
49418  * The default SelectionModel used by {@link Roo.grid.Grid}.
49419  * It supports multiple selections and keyboard selection/navigation. 
49420  * @constructor
49421  * @param {Object} config
49422  */
49423 Roo.grid.RowSelectionModel = function(config){
49424     Roo.apply(this, config);
49425     this.selections = new Roo.util.MixedCollection(false, function(o){
49426         return o.id;
49427     });
49428
49429     this.last = false;
49430     this.lastActive = false;
49431
49432     this.addEvents({
49433         /**
49434              * @event selectionchange
49435              * Fires when the selection changes
49436              * @param {SelectionModel} this
49437              */
49438             "selectionchange" : true,
49439         /**
49440              * @event afterselectionchange
49441              * Fires after the selection changes (eg. by key press or clicking)
49442              * @param {SelectionModel} this
49443              */
49444             "afterselectionchange" : true,
49445         /**
49446              * @event beforerowselect
49447              * Fires when a row is selected being selected, return false to cancel.
49448              * @param {SelectionModel} this
49449              * @param {Number} rowIndex The selected index
49450              * @param {Boolean} keepExisting False if other selections will be cleared
49451              */
49452             "beforerowselect" : true,
49453         /**
49454              * @event rowselect
49455              * Fires when a row is selected.
49456              * @param {SelectionModel} this
49457              * @param {Number} rowIndex The selected index
49458              * @param {Roo.data.Record} r The record
49459              */
49460             "rowselect" : true,
49461         /**
49462              * @event rowdeselect
49463              * Fires when a row is deselected.
49464              * @param {SelectionModel} this
49465              * @param {Number} rowIndex The selected index
49466              */
49467         "rowdeselect" : true
49468     });
49469     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49470     this.locked = false;
49471 };
49472
49473 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49474     /**
49475      * @cfg {Boolean} singleSelect
49476      * True to allow selection of only one row at a time (defaults to false)
49477      */
49478     singleSelect : false,
49479
49480     // private
49481     initEvents : function(){
49482
49483         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49484             this.grid.on("mousedown", this.handleMouseDown, this);
49485         }else{ // allow click to work like normal
49486             this.grid.on("rowclick", this.handleDragableRowClick, this);
49487         }
49488
49489         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49490             "up" : function(e){
49491                 if(!e.shiftKey){
49492                     this.selectPrevious(e.shiftKey);
49493                 }else if(this.last !== false && this.lastActive !== false){
49494                     var last = this.last;
49495                     this.selectRange(this.last,  this.lastActive-1);
49496                     this.grid.getView().focusRow(this.lastActive);
49497                     if(last !== false){
49498                         this.last = last;
49499                     }
49500                 }else{
49501                     this.selectFirstRow();
49502                 }
49503                 this.fireEvent("afterselectionchange", this);
49504             },
49505             "down" : function(e){
49506                 if(!e.shiftKey){
49507                     this.selectNext(e.shiftKey);
49508                 }else if(this.last !== false && this.lastActive !== false){
49509                     var last = this.last;
49510                     this.selectRange(this.last,  this.lastActive+1);
49511                     this.grid.getView().focusRow(this.lastActive);
49512                     if(last !== false){
49513                         this.last = last;
49514                     }
49515                 }else{
49516                     this.selectFirstRow();
49517                 }
49518                 this.fireEvent("afterselectionchange", this);
49519             },
49520             scope: this
49521         });
49522
49523         var view = this.grid.view;
49524         view.on("refresh", this.onRefresh, this);
49525         view.on("rowupdated", this.onRowUpdated, this);
49526         view.on("rowremoved", this.onRemove, this);
49527     },
49528
49529     // private
49530     onRefresh : function(){
49531         var ds = this.grid.dataSource, i, v = this.grid.view;
49532         var s = this.selections;
49533         s.each(function(r){
49534             if((i = ds.indexOfId(r.id)) != -1){
49535                 v.onRowSelect(i);
49536             }else{
49537                 s.remove(r);
49538             }
49539         });
49540     },
49541
49542     // private
49543     onRemove : function(v, index, r){
49544         this.selections.remove(r);
49545     },
49546
49547     // private
49548     onRowUpdated : function(v, index, r){
49549         if(this.isSelected(r)){
49550             v.onRowSelect(index);
49551         }
49552     },
49553
49554     /**
49555      * Select records.
49556      * @param {Array} records The records to select
49557      * @param {Boolean} keepExisting (optional) True to keep existing selections
49558      */
49559     selectRecords : function(records, keepExisting){
49560         if(!keepExisting){
49561             this.clearSelections();
49562         }
49563         var ds = this.grid.dataSource;
49564         for(var i = 0, len = records.length; i < len; i++){
49565             this.selectRow(ds.indexOf(records[i]), true);
49566         }
49567     },
49568
49569     /**
49570      * Gets the number of selected rows.
49571      * @return {Number}
49572      */
49573     getCount : function(){
49574         return this.selections.length;
49575     },
49576
49577     /**
49578      * Selects the first row in the grid.
49579      */
49580     selectFirstRow : function(){
49581         this.selectRow(0);
49582     },
49583
49584     /**
49585      * Select the last row.
49586      * @param {Boolean} keepExisting (optional) True to keep existing selections
49587      */
49588     selectLastRow : function(keepExisting){
49589         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49590     },
49591
49592     /**
49593      * Selects the row immediately following the last selected row.
49594      * @param {Boolean} keepExisting (optional) True to keep existing selections
49595      */
49596     selectNext : function(keepExisting){
49597         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49598             this.selectRow(this.last+1, keepExisting);
49599             this.grid.getView().focusRow(this.last);
49600         }
49601     },
49602
49603     /**
49604      * Selects the row that precedes the last selected row.
49605      * @param {Boolean} keepExisting (optional) True to keep existing selections
49606      */
49607     selectPrevious : function(keepExisting){
49608         if(this.last){
49609             this.selectRow(this.last-1, keepExisting);
49610             this.grid.getView().focusRow(this.last);
49611         }
49612     },
49613
49614     /**
49615      * Returns the selected records
49616      * @return {Array} Array of selected records
49617      */
49618     getSelections : function(){
49619         return [].concat(this.selections.items);
49620     },
49621
49622     /**
49623      * Returns the first selected record.
49624      * @return {Record}
49625      */
49626     getSelected : function(){
49627         return this.selections.itemAt(0);
49628     },
49629
49630
49631     /**
49632      * Clears all selections.
49633      */
49634     clearSelections : function(fast){
49635         if(this.locked) return;
49636         if(fast !== true){
49637             var ds = this.grid.dataSource;
49638             var s = this.selections;
49639             s.each(function(r){
49640                 this.deselectRow(ds.indexOfId(r.id));
49641             }, this);
49642             s.clear();
49643         }else{
49644             this.selections.clear();
49645         }
49646         this.last = false;
49647     },
49648
49649
49650     /**
49651      * Selects all rows.
49652      */
49653     selectAll : function(){
49654         if(this.locked) return;
49655         this.selections.clear();
49656         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49657             this.selectRow(i, true);
49658         }
49659     },
49660
49661     /**
49662      * Returns True if there is a selection.
49663      * @return {Boolean}
49664      */
49665     hasSelection : function(){
49666         return this.selections.length > 0;
49667     },
49668
49669     /**
49670      * Returns True if the specified row is selected.
49671      * @param {Number/Record} record The record or index of the record to check
49672      * @return {Boolean}
49673      */
49674     isSelected : function(index){
49675         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49676         return (r && this.selections.key(r.id) ? true : false);
49677     },
49678
49679     /**
49680      * Returns True if the specified record id is selected.
49681      * @param {String} id The id of record to check
49682      * @return {Boolean}
49683      */
49684     isIdSelected : function(id){
49685         return (this.selections.key(id) ? true : false);
49686     },
49687
49688     // private
49689     handleMouseDown : function(e, t){
49690         var view = this.grid.getView(), rowIndex;
49691         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49692             return;
49693         };
49694         if(e.shiftKey && this.last !== false){
49695             var last = this.last;
49696             this.selectRange(last, rowIndex, e.ctrlKey);
49697             this.last = last; // reset the last
49698             view.focusRow(rowIndex);
49699         }else{
49700             var isSelected = this.isSelected(rowIndex);
49701             if(e.button !== 0 && isSelected){
49702                 view.focusRow(rowIndex);
49703             }else if(e.ctrlKey && isSelected){
49704                 this.deselectRow(rowIndex);
49705             }else if(!isSelected){
49706                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49707                 view.focusRow(rowIndex);
49708             }
49709         }
49710         this.fireEvent("afterselectionchange", this);
49711     },
49712     // private
49713     handleDragableRowClick :  function(grid, rowIndex, e) 
49714     {
49715         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49716             this.selectRow(rowIndex, false);
49717             grid.view.focusRow(rowIndex);
49718              this.fireEvent("afterselectionchange", this);
49719         }
49720     },
49721     
49722     /**
49723      * Selects multiple rows.
49724      * @param {Array} rows Array of the indexes of the row to select
49725      * @param {Boolean} keepExisting (optional) True to keep existing selections
49726      */
49727     selectRows : function(rows, keepExisting){
49728         if(!keepExisting){
49729             this.clearSelections();
49730         }
49731         for(var i = 0, len = rows.length; i < len; i++){
49732             this.selectRow(rows[i], true);
49733         }
49734     },
49735
49736     /**
49737      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49738      * @param {Number} startRow The index of the first row in the range
49739      * @param {Number} endRow The index of the last row in the range
49740      * @param {Boolean} keepExisting (optional) True to retain existing selections
49741      */
49742     selectRange : function(startRow, endRow, keepExisting){
49743         if(this.locked) return;
49744         if(!keepExisting){
49745             this.clearSelections();
49746         }
49747         if(startRow <= endRow){
49748             for(var i = startRow; i <= endRow; i++){
49749                 this.selectRow(i, true);
49750             }
49751         }else{
49752             for(var i = startRow; i >= endRow; i--){
49753                 this.selectRow(i, true);
49754             }
49755         }
49756     },
49757
49758     /**
49759      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49760      * @param {Number} startRow The index of the first row in the range
49761      * @param {Number} endRow The index of the last row in the range
49762      */
49763     deselectRange : function(startRow, endRow, preventViewNotify){
49764         if(this.locked) return;
49765         for(var i = startRow; i <= endRow; i++){
49766             this.deselectRow(i, preventViewNotify);
49767         }
49768     },
49769
49770     /**
49771      * Selects a row.
49772      * @param {Number} row The index of the row to select
49773      * @param {Boolean} keepExisting (optional) True to keep existing selections
49774      */
49775     selectRow : function(index, keepExisting, preventViewNotify){
49776         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49777         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49778             if(!keepExisting || this.singleSelect){
49779                 this.clearSelections();
49780             }
49781             var r = this.grid.dataSource.getAt(index);
49782             this.selections.add(r);
49783             this.last = this.lastActive = index;
49784             if(!preventViewNotify){
49785                 this.grid.getView().onRowSelect(index);
49786             }
49787             this.fireEvent("rowselect", this, index, r);
49788             this.fireEvent("selectionchange", this);
49789         }
49790     },
49791
49792     /**
49793      * Deselects a row.
49794      * @param {Number} row The index of the row to deselect
49795      */
49796     deselectRow : function(index, preventViewNotify){
49797         if(this.locked) return;
49798         if(this.last == index){
49799             this.last = false;
49800         }
49801         if(this.lastActive == index){
49802             this.lastActive = false;
49803         }
49804         var r = this.grid.dataSource.getAt(index);
49805         this.selections.remove(r);
49806         if(!preventViewNotify){
49807             this.grid.getView().onRowDeselect(index);
49808         }
49809         this.fireEvent("rowdeselect", this, index);
49810         this.fireEvent("selectionchange", this);
49811     },
49812
49813     // private
49814     restoreLast : function(){
49815         if(this._last){
49816             this.last = this._last;
49817         }
49818     },
49819
49820     // private
49821     acceptsNav : function(row, col, cm){
49822         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49823     },
49824
49825     // private
49826     onEditorKey : function(field, e){
49827         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49828         if(k == e.TAB){
49829             e.stopEvent();
49830             ed.completeEdit();
49831             if(e.shiftKey){
49832                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49833             }else{
49834                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49835             }
49836         }else if(k == e.ENTER && !e.ctrlKey){
49837             e.stopEvent();
49838             ed.completeEdit();
49839             if(e.shiftKey){
49840                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49841             }else{
49842                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49843             }
49844         }else if(k == e.ESC){
49845             ed.cancelEdit();
49846         }
49847         if(newCell){
49848             g.startEditing(newCell[0], newCell[1]);
49849         }
49850     }
49851 });/*
49852  * Based on:
49853  * Ext JS Library 1.1.1
49854  * Copyright(c) 2006-2007, Ext JS, LLC.
49855  *
49856  * Originally Released Under LGPL - original licence link has changed is not relivant.
49857  *
49858  * Fork - LGPL
49859  * <script type="text/javascript">
49860  */
49861 /**
49862  * @class Roo.grid.CellSelectionModel
49863  * @extends Roo.grid.AbstractSelectionModel
49864  * This class provides the basic implementation for cell selection in a grid.
49865  * @constructor
49866  * @param {Object} config The object containing the configuration of this model.
49867  */
49868 Roo.grid.CellSelectionModel = function(config){
49869     Roo.apply(this, config);
49870
49871     this.selection = null;
49872
49873     this.addEvents({
49874         /**
49875              * @event beforerowselect
49876              * Fires before a cell is selected.
49877              * @param {SelectionModel} this
49878              * @param {Number} rowIndex The selected row index
49879              * @param {Number} colIndex The selected cell index
49880              */
49881             "beforecellselect" : true,
49882         /**
49883              * @event cellselect
49884              * Fires when a cell is selected.
49885              * @param {SelectionModel} this
49886              * @param {Number} rowIndex The selected row index
49887              * @param {Number} colIndex The selected cell index
49888              */
49889             "cellselect" : true,
49890         /**
49891              * @event selectionchange
49892              * Fires when the active selection changes.
49893              * @param {SelectionModel} this
49894              * @param {Object} selection null for no selection or an object (o) with two properties
49895                 <ul>
49896                 <li>o.record: the record object for the row the selection is in</li>
49897                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49898                 </ul>
49899              */
49900             "selectionchange" : true
49901     });
49902     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49903 };
49904
49905 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49906
49907     /** @ignore */
49908     initEvents : function(){
49909         this.grid.on("mousedown", this.handleMouseDown, this);
49910         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49911         var view = this.grid.view;
49912         view.on("refresh", this.onViewChange, this);
49913         view.on("rowupdated", this.onRowUpdated, this);
49914         view.on("beforerowremoved", this.clearSelections, this);
49915         view.on("beforerowsinserted", this.clearSelections, this);
49916         if(this.grid.isEditor){
49917             this.grid.on("beforeedit", this.beforeEdit,  this);
49918         }
49919     },
49920
49921         //private
49922     beforeEdit : function(e){
49923         this.select(e.row, e.column, false, true, e.record);
49924     },
49925
49926         //private
49927     onRowUpdated : function(v, index, r){
49928         if(this.selection && this.selection.record == r){
49929             v.onCellSelect(index, this.selection.cell[1]);
49930         }
49931     },
49932
49933         //private
49934     onViewChange : function(){
49935         this.clearSelections(true);
49936     },
49937
49938         /**
49939          * Returns the currently selected cell,.
49940          * @return {Array} The selected cell (row, column) or null if none selected.
49941          */
49942     getSelectedCell : function(){
49943         return this.selection ? this.selection.cell : null;
49944     },
49945
49946     /**
49947      * Clears all selections.
49948      * @param {Boolean} true to prevent the gridview from being notified about the change.
49949      */
49950     clearSelections : function(preventNotify){
49951         var s = this.selection;
49952         if(s){
49953             if(preventNotify !== true){
49954                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49955             }
49956             this.selection = null;
49957             this.fireEvent("selectionchange", this, null);
49958         }
49959     },
49960
49961     /**
49962      * Returns true if there is a selection.
49963      * @return {Boolean}
49964      */
49965     hasSelection : function(){
49966         return this.selection ? true : false;
49967     },
49968
49969     /** @ignore */
49970     handleMouseDown : function(e, t){
49971         var v = this.grid.getView();
49972         if(this.isLocked()){
49973             return;
49974         };
49975         var row = v.findRowIndex(t);
49976         var cell = v.findCellIndex(t);
49977         if(row !== false && cell !== false){
49978             this.select(row, cell);
49979         }
49980     },
49981
49982     /**
49983      * Selects a cell.
49984      * @param {Number} rowIndex
49985      * @param {Number} collIndex
49986      */
49987     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49988         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49989             this.clearSelections();
49990             r = r || this.grid.dataSource.getAt(rowIndex);
49991             this.selection = {
49992                 record : r,
49993                 cell : [rowIndex, colIndex]
49994             };
49995             if(!preventViewNotify){
49996                 var v = this.grid.getView();
49997                 v.onCellSelect(rowIndex, colIndex);
49998                 if(preventFocus !== true){
49999                     v.focusCell(rowIndex, colIndex);
50000                 }
50001             }
50002             this.fireEvent("cellselect", this, rowIndex, colIndex);
50003             this.fireEvent("selectionchange", this, this.selection);
50004         }
50005     },
50006
50007         //private
50008     isSelectable : function(rowIndex, colIndex, cm){
50009         return !cm.isHidden(colIndex);
50010     },
50011
50012     /** @ignore */
50013     handleKeyDown : function(e){
50014         Roo.log('Cell Sel Model handleKeyDown');
50015         if(!e.isNavKeyPress()){
50016             return;
50017         }
50018         var g = this.grid, s = this.selection;
50019         if(!s){
50020             e.stopEvent();
50021             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50022             if(cell){
50023                 this.select(cell[0], cell[1]);
50024             }
50025             return;
50026         }
50027         var sm = this;
50028         var walk = function(row, col, step){
50029             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50030         };
50031         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50032         var newCell;
50033
50034         switch(k){
50035             case e.TAB:
50036                 // handled by onEditorKey
50037                 if (g.isEditor && g.editing) {
50038                     return;
50039                 }
50040                 if(e.shiftKey){
50041                      newCell = walk(r, c-1, -1);
50042                 }else{
50043                      newCell = walk(r, c+1, 1);
50044                 }
50045              break;
50046              case e.DOWN:
50047                  newCell = walk(r+1, c, 1);
50048              break;
50049              case e.UP:
50050                  newCell = walk(r-1, c, -1);
50051              break;
50052              case e.RIGHT:
50053                  newCell = walk(r, c+1, 1);
50054              break;
50055              case e.LEFT:
50056                  newCell = walk(r, c-1, -1);
50057              break;
50058              case e.ENTER:
50059                  if(g.isEditor && !g.editing){
50060                     g.startEditing(r, c);
50061                     e.stopEvent();
50062                     return;
50063                 }
50064              break;
50065         };
50066         if(newCell){
50067             this.select(newCell[0], newCell[1]);
50068             e.stopEvent();
50069         }
50070     },
50071
50072     acceptsNav : function(row, col, cm){
50073         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50074     },
50075
50076     onEditorKey : function(field, e){
50077         
50078         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50079         ///Roo.log('onEditorKey' + k);
50080         
50081         if(k == e.TAB){
50082             if(e.shiftKey){
50083                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50084             }else{
50085                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50086             }
50087             e.stopEvent();
50088         }else if(k == e.ENTER && !e.ctrlKey){
50089             ed.completeEdit();
50090             e.stopEvent();
50091             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50092         }else if(k == e.ESC){
50093             ed.cancelEdit();
50094         }
50095         
50096         
50097         if(newCell){
50098             //Roo.log('next cell after edit');
50099             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50100         }
50101     }
50102 });/*
50103  * Based on:
50104  * Ext JS Library 1.1.1
50105  * Copyright(c) 2006-2007, Ext JS, LLC.
50106  *
50107  * Originally Released Under LGPL - original licence link has changed is not relivant.
50108  *
50109  * Fork - LGPL
50110  * <script type="text/javascript">
50111  */
50112  
50113 /**
50114  * @class Roo.grid.EditorGrid
50115  * @extends Roo.grid.Grid
50116  * Class for creating and editable grid.
50117  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50118  * The container MUST have some type of size defined for the grid to fill. The container will be 
50119  * automatically set to position relative if it isn't already.
50120  * @param {Object} dataSource The data model to bind to
50121  * @param {Object} colModel The column model with info about this grid's columns
50122  */
50123 Roo.grid.EditorGrid = function(container, config){
50124     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50125     this.getGridEl().addClass("xedit-grid");
50126
50127     if(!this.selModel){
50128         this.selModel = new Roo.grid.CellSelectionModel();
50129     }
50130
50131     this.activeEditor = null;
50132
50133         this.addEvents({
50134             /**
50135              * @event beforeedit
50136              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50137              * <ul style="padding:5px;padding-left:16px;">
50138              * <li>grid - This grid</li>
50139              * <li>record - The record being edited</li>
50140              * <li>field - The field name being edited</li>
50141              * <li>value - The value for the field being edited.</li>
50142              * <li>row - The grid row index</li>
50143              * <li>column - The grid column index</li>
50144              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50145              * </ul>
50146              * @param {Object} e An edit event (see above for description)
50147              */
50148             "beforeedit" : true,
50149             /**
50150              * @event afteredit
50151              * Fires after a cell is edited. <br />
50152              * <ul style="padding:5px;padding-left:16px;">
50153              * <li>grid - This grid</li>
50154              * <li>record - The record being edited</li>
50155              * <li>field - The field name being edited</li>
50156              * <li>value - The value being set</li>
50157              * <li>originalValue - The original value for the field, before the edit.</li>
50158              * <li>row - The grid row index</li>
50159              * <li>column - The grid column index</li>
50160              * </ul>
50161              * @param {Object} e An edit event (see above for description)
50162              */
50163             "afteredit" : true,
50164             /**
50165              * @event validateedit
50166              * Fires after a cell is edited, but before the value is set in the record. 
50167          * You can use this to modify the value being set in the field, Return false
50168              * to cancel the change. The edit event object has the following properties <br />
50169              * <ul style="padding:5px;padding-left:16px;">
50170          * <li>editor - This editor</li>
50171              * <li>grid - This grid</li>
50172              * <li>record - The record being edited</li>
50173              * <li>field - The field name being edited</li>
50174              * <li>value - The value being set</li>
50175              * <li>originalValue - The original value for the field, before the edit.</li>
50176              * <li>row - The grid row index</li>
50177              * <li>column - The grid column index</li>
50178              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50179              * </ul>
50180              * @param {Object} e An edit event (see above for description)
50181              */
50182             "validateedit" : true
50183         });
50184     this.on("bodyscroll", this.stopEditing,  this);
50185     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50186 };
50187
50188 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50189     /**
50190      * @cfg {Number} clicksToEdit
50191      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50192      */
50193     clicksToEdit: 2,
50194
50195     // private
50196     isEditor : true,
50197     // private
50198     trackMouseOver: false, // causes very odd FF errors
50199
50200     onCellDblClick : function(g, row, col){
50201         this.startEditing(row, col);
50202     },
50203
50204     onEditComplete : function(ed, value, startValue){
50205         this.editing = false;
50206         this.activeEditor = null;
50207         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50208         var r = ed.record;
50209         var field = this.colModel.getDataIndex(ed.col);
50210         var e = {
50211             grid: this,
50212             record: r,
50213             field: field,
50214             originalValue: startValue,
50215             value: value,
50216             row: ed.row,
50217             column: ed.col,
50218             cancel:false,
50219             editor: ed
50220         };
50221         if(String(value) !== String(startValue)){
50222             
50223             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50224                 r.set(field, e.value);
50225                 // if we are dealing with a combo box..
50226                 // then we also set the 'name' colum to be the displayField
50227                 if (ed.field.displayField && ed.field.name) {
50228                     r.set(ed.field.name, ed.field.el.dom.value);
50229                 }
50230                 
50231                 delete e.cancel; //?? why!!!
50232                 this.fireEvent("afteredit", e);
50233             }
50234         } else {
50235             this.fireEvent("afteredit", e); // always fire it!
50236         }
50237         this.view.focusCell(ed.row, ed.col);
50238     },
50239
50240     /**
50241      * Starts editing the specified for the specified row/column
50242      * @param {Number} rowIndex
50243      * @param {Number} colIndex
50244      */
50245     startEditing : function(row, col){
50246         this.stopEditing();
50247         if(this.colModel.isCellEditable(col, row)){
50248             this.view.ensureVisible(row, col, true);
50249             var r = this.dataSource.getAt(row);
50250             var field = this.colModel.getDataIndex(col);
50251             var e = {
50252                 grid: this,
50253                 record: r,
50254                 field: field,
50255                 value: r.data[field],
50256                 row: row,
50257                 column: col,
50258                 cancel:false
50259             };
50260             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50261                 this.editing = true;
50262                 var ed = this.colModel.getCellEditor(col, row);
50263                 
50264                 if (!ed) {
50265                     return;
50266                 }
50267                 if(!ed.rendered){
50268                     ed.render(ed.parentEl || document.body);
50269                 }
50270                 ed.field.reset();
50271                 (function(){ // complex but required for focus issues in safari, ie and opera
50272                     ed.row = row;
50273                     ed.col = col;
50274                     ed.record = r;
50275                     ed.on("complete", this.onEditComplete, this, {single: true});
50276                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50277                     this.activeEditor = ed;
50278                     var v = r.data[field];
50279                     ed.startEdit(this.view.getCell(row, col), v);
50280                     // combo's with 'displayField and name set
50281                     if (ed.field.displayField && ed.field.name) {
50282                         ed.field.el.dom.value = r.data[ed.field.name];
50283                     }
50284                     
50285                     
50286                 }).defer(50, this);
50287             }
50288         }
50289     },
50290         
50291     /**
50292      * Stops any active editing
50293      */
50294     stopEditing : function(){
50295         if(this.activeEditor){
50296             this.activeEditor.completeEdit();
50297         }
50298         this.activeEditor = null;
50299     }
50300 });/*
50301  * Based on:
50302  * Ext JS Library 1.1.1
50303  * Copyright(c) 2006-2007, Ext JS, LLC.
50304  *
50305  * Originally Released Under LGPL - original licence link has changed is not relivant.
50306  *
50307  * Fork - LGPL
50308  * <script type="text/javascript">
50309  */
50310
50311 // private - not really -- you end up using it !
50312 // This is a support class used internally by the Grid components
50313
50314 /**
50315  * @class Roo.grid.GridEditor
50316  * @extends Roo.Editor
50317  * Class for creating and editable grid elements.
50318  * @param {Object} config any settings (must include field)
50319  */
50320 Roo.grid.GridEditor = function(field, config){
50321     if (!config && field.field) {
50322         config = field;
50323         field = Roo.factory(config.field, Roo.form);
50324     }
50325     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50326     field.monitorTab = false;
50327 };
50328
50329 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50330     
50331     /**
50332      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50333      */
50334     
50335     alignment: "tl-tl",
50336     autoSize: "width",
50337     hideEl : false,
50338     cls: "x-small-editor x-grid-editor",
50339     shim:false,
50340     shadow:"frame"
50341 });/*
50342  * Based on:
50343  * Ext JS Library 1.1.1
50344  * Copyright(c) 2006-2007, Ext JS, LLC.
50345  *
50346  * Originally Released Under LGPL - original licence link has changed is not relivant.
50347  *
50348  * Fork - LGPL
50349  * <script type="text/javascript">
50350  */
50351   
50352
50353   
50354 Roo.grid.PropertyRecord = Roo.data.Record.create([
50355     {name:'name',type:'string'},  'value'
50356 ]);
50357
50358
50359 Roo.grid.PropertyStore = function(grid, source){
50360     this.grid = grid;
50361     this.store = new Roo.data.Store({
50362         recordType : Roo.grid.PropertyRecord
50363     });
50364     this.store.on('update', this.onUpdate,  this);
50365     if(source){
50366         this.setSource(source);
50367     }
50368     Roo.grid.PropertyStore.superclass.constructor.call(this);
50369 };
50370
50371
50372
50373 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50374     setSource : function(o){
50375         this.source = o;
50376         this.store.removeAll();
50377         var data = [];
50378         for(var k in o){
50379             if(this.isEditableValue(o[k])){
50380                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50381             }
50382         }
50383         this.store.loadRecords({records: data}, {}, true);
50384     },
50385
50386     onUpdate : function(ds, record, type){
50387         if(type == Roo.data.Record.EDIT){
50388             var v = record.data['value'];
50389             var oldValue = record.modified['value'];
50390             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50391                 this.source[record.id] = v;
50392                 record.commit();
50393                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50394             }else{
50395                 record.reject();
50396             }
50397         }
50398     },
50399
50400     getProperty : function(row){
50401        return this.store.getAt(row);
50402     },
50403
50404     isEditableValue: function(val){
50405         if(val && val instanceof Date){
50406             return true;
50407         }else if(typeof val == 'object' || typeof val == 'function'){
50408             return false;
50409         }
50410         return true;
50411     },
50412
50413     setValue : function(prop, value){
50414         this.source[prop] = value;
50415         this.store.getById(prop).set('value', value);
50416     },
50417
50418     getSource : function(){
50419         return this.source;
50420     }
50421 });
50422
50423 Roo.grid.PropertyColumnModel = function(grid, store){
50424     this.grid = grid;
50425     var g = Roo.grid;
50426     g.PropertyColumnModel.superclass.constructor.call(this, [
50427         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50428         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50429     ]);
50430     this.store = store;
50431     this.bselect = Roo.DomHelper.append(document.body, {
50432         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50433             {tag: 'option', value: 'true', html: 'true'},
50434             {tag: 'option', value: 'false', html: 'false'}
50435         ]
50436     });
50437     Roo.id(this.bselect);
50438     var f = Roo.form;
50439     this.editors = {
50440         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50441         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50442         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50443         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50444         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50445     };
50446     this.renderCellDelegate = this.renderCell.createDelegate(this);
50447     this.renderPropDelegate = this.renderProp.createDelegate(this);
50448 };
50449
50450 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50451     
50452     
50453     nameText : 'Name',
50454     valueText : 'Value',
50455     
50456     dateFormat : 'm/j/Y',
50457     
50458     
50459     renderDate : function(dateVal){
50460         return dateVal.dateFormat(this.dateFormat);
50461     },
50462
50463     renderBool : function(bVal){
50464         return bVal ? 'true' : 'false';
50465     },
50466
50467     isCellEditable : function(colIndex, rowIndex){
50468         return colIndex == 1;
50469     },
50470
50471     getRenderer : function(col){
50472         return col == 1 ?
50473             this.renderCellDelegate : this.renderPropDelegate;
50474     },
50475
50476     renderProp : function(v){
50477         return this.getPropertyName(v);
50478     },
50479
50480     renderCell : function(val){
50481         var rv = val;
50482         if(val instanceof Date){
50483             rv = this.renderDate(val);
50484         }else if(typeof val == 'boolean'){
50485             rv = this.renderBool(val);
50486         }
50487         return Roo.util.Format.htmlEncode(rv);
50488     },
50489
50490     getPropertyName : function(name){
50491         var pn = this.grid.propertyNames;
50492         return pn && pn[name] ? pn[name] : name;
50493     },
50494
50495     getCellEditor : function(colIndex, rowIndex){
50496         var p = this.store.getProperty(rowIndex);
50497         var n = p.data['name'], val = p.data['value'];
50498         
50499         if(typeof(this.grid.customEditors[n]) == 'string'){
50500             return this.editors[this.grid.customEditors[n]];
50501         }
50502         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50503             return this.grid.customEditors[n];
50504         }
50505         if(val instanceof Date){
50506             return this.editors['date'];
50507         }else if(typeof val == 'number'){
50508             return this.editors['number'];
50509         }else if(typeof val == 'boolean'){
50510             return this.editors['boolean'];
50511         }else{
50512             return this.editors['string'];
50513         }
50514     }
50515 });
50516
50517 /**
50518  * @class Roo.grid.PropertyGrid
50519  * @extends Roo.grid.EditorGrid
50520  * This class represents the  interface of a component based property grid control.
50521  * <br><br>Usage:<pre><code>
50522  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50523       
50524  });
50525  // set any options
50526  grid.render();
50527  * </code></pre>
50528   
50529  * @constructor
50530  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50531  * The container MUST have some type of size defined for the grid to fill. The container will be
50532  * automatically set to position relative if it isn't already.
50533  * @param {Object} config A config object that sets properties on this grid.
50534  */
50535 Roo.grid.PropertyGrid = function(container, config){
50536     config = config || {};
50537     var store = new Roo.grid.PropertyStore(this);
50538     this.store = store;
50539     var cm = new Roo.grid.PropertyColumnModel(this, store);
50540     store.store.sort('name', 'ASC');
50541     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50542         ds: store.store,
50543         cm: cm,
50544         enableColLock:false,
50545         enableColumnMove:false,
50546         stripeRows:false,
50547         trackMouseOver: false,
50548         clicksToEdit:1
50549     }, config));
50550     this.getGridEl().addClass('x-props-grid');
50551     this.lastEditRow = null;
50552     this.on('columnresize', this.onColumnResize, this);
50553     this.addEvents({
50554          /**
50555              * @event beforepropertychange
50556              * Fires before a property changes (return false to stop?)
50557              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50558              * @param {String} id Record Id
50559              * @param {String} newval New Value
50560          * @param {String} oldval Old Value
50561              */
50562         "beforepropertychange": true,
50563         /**
50564              * @event propertychange
50565              * Fires after a property changes
50566              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50567              * @param {String} id Record Id
50568              * @param {String} newval New Value
50569          * @param {String} oldval Old Value
50570              */
50571         "propertychange": true
50572     });
50573     this.customEditors = this.customEditors || {};
50574 };
50575 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50576     
50577      /**
50578      * @cfg {Object} customEditors map of colnames=> custom editors.
50579      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50580      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50581      * false disables editing of the field.
50582          */
50583     
50584       /**
50585      * @cfg {Object} propertyNames map of property Names to their displayed value
50586          */
50587     
50588     render : function(){
50589         Roo.grid.PropertyGrid.superclass.render.call(this);
50590         this.autoSize.defer(100, this);
50591     },
50592
50593     autoSize : function(){
50594         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50595         if(this.view){
50596             this.view.fitColumns();
50597         }
50598     },
50599
50600     onColumnResize : function(){
50601         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50602         this.autoSize();
50603     },
50604     /**
50605      * Sets the data for the Grid
50606      * accepts a Key => Value object of all the elements avaiable.
50607      * @param {Object} data  to appear in grid.
50608      */
50609     setSource : function(source){
50610         this.store.setSource(source);
50611         //this.autoSize();
50612     },
50613     /**
50614      * Gets all the data from the grid.
50615      * @return {Object} data  data stored in grid
50616      */
50617     getSource : function(){
50618         return this.store.getSource();
50619     }
50620 });/*
50621  * Based on:
50622  * Ext JS Library 1.1.1
50623  * Copyright(c) 2006-2007, Ext JS, LLC.
50624  *
50625  * Originally Released Under LGPL - original licence link has changed is not relivant.
50626  *
50627  * Fork - LGPL
50628  * <script type="text/javascript">
50629  */
50630  
50631 /**
50632  * @class Roo.LoadMask
50633  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50634  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50635  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50636  * element's UpdateManager load indicator and will be destroyed after the initial load.
50637  * @constructor
50638  * Create a new LoadMask
50639  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50640  * @param {Object} config The config object
50641  */
50642 Roo.LoadMask = function(el, config){
50643     this.el = Roo.get(el);
50644     Roo.apply(this, config);
50645     if(this.store){
50646         this.store.on('beforeload', this.onBeforeLoad, this);
50647         this.store.on('load', this.onLoad, this);
50648         this.store.on('loadexception', this.onLoad, this);
50649         this.removeMask = false;
50650     }else{
50651         var um = this.el.getUpdateManager();
50652         um.showLoadIndicator = false; // disable the default indicator
50653         um.on('beforeupdate', this.onBeforeLoad, this);
50654         um.on('update', this.onLoad, this);
50655         um.on('failure', this.onLoad, this);
50656         this.removeMask = true;
50657     }
50658 };
50659
50660 Roo.LoadMask.prototype = {
50661     /**
50662      * @cfg {Boolean} removeMask
50663      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50664      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50665      */
50666     /**
50667      * @cfg {String} msg
50668      * The text to display in a centered loading message box (defaults to 'Loading...')
50669      */
50670     msg : 'Loading...',
50671     /**
50672      * @cfg {String} msgCls
50673      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50674      */
50675     msgCls : 'x-mask-loading',
50676
50677     /**
50678      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50679      * @type Boolean
50680      */
50681     disabled: false,
50682
50683     /**
50684      * Disables the mask to prevent it from being displayed
50685      */
50686     disable : function(){
50687        this.disabled = true;
50688     },
50689
50690     /**
50691      * Enables the mask so that it can be displayed
50692      */
50693     enable : function(){
50694         this.disabled = false;
50695     },
50696
50697     // private
50698     onLoad : function(){
50699         this.el.unmask(this.removeMask);
50700     },
50701
50702     // private
50703     onBeforeLoad : function(){
50704         if(!this.disabled){
50705             this.el.mask(this.msg, this.msgCls);
50706         }
50707     },
50708
50709     // private
50710     destroy : function(){
50711         if(this.store){
50712             this.store.un('beforeload', this.onBeforeLoad, this);
50713             this.store.un('load', this.onLoad, this);
50714             this.store.un('loadexception', this.onLoad, this);
50715         }else{
50716             var um = this.el.getUpdateManager();
50717             um.un('beforeupdate', this.onBeforeLoad, this);
50718             um.un('update', this.onLoad, this);
50719             um.un('failure', this.onLoad, this);
50720         }
50721     }
50722 };/*
50723  * Based on:
50724  * Ext JS Library 1.1.1
50725  * Copyright(c) 2006-2007, Ext JS, LLC.
50726  *
50727  * Originally Released Under LGPL - original licence link has changed is not relivant.
50728  *
50729  * Fork - LGPL
50730  * <script type="text/javascript">
50731  */
50732 Roo.XTemplate = function(){
50733     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50734     var s = this.html;
50735
50736     s = ['<tpl>', s, '</tpl>'].join('');
50737
50738     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50739
50740     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50741     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50742     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50743     var m, id = 0;
50744     var tpls = [];
50745
50746     while(m = s.match(re)){
50747        var m2 = m[0].match(nameRe);
50748        var m3 = m[0].match(ifRe);
50749        var m4 = m[0].match(execRe);
50750        var exp = null, fn = null, exec = null;
50751        var name = m2 && m2[1] ? m2[1] : '';
50752        if(m3){
50753            exp = m3 && m3[1] ? m3[1] : null;
50754            if(exp){
50755                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50756            }
50757        }
50758        if(m4){
50759            exp = m4 && m4[1] ? m4[1] : null;
50760            if(exp){
50761                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50762            }
50763        }
50764        if(name){
50765            switch(name){
50766                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50767                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50768                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50769            }
50770        }
50771        tpls.push({
50772             id: id,
50773             target: name,
50774             exec: exec,
50775             test: fn,
50776             body: m[1]||''
50777         });
50778        s = s.replace(m[0], '{xtpl'+ id + '}');
50779        ++id;
50780     }
50781     for(var i = tpls.length-1; i >= 0; --i){
50782         this.compileTpl(tpls[i]);
50783     }
50784     this.master = tpls[tpls.length-1];
50785     this.tpls = tpls;
50786 };
50787 Roo.extend(Roo.XTemplate, Roo.Template, {
50788
50789     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50790
50791     applySubTemplate : function(id, values, parent){
50792         var t = this.tpls[id];
50793         if(t.test && !t.test.call(this, values, parent)){
50794             return '';
50795         }
50796         if(t.exec && t.exec.call(this, values, parent)){
50797             return '';
50798         }
50799         var vs = t.target ? t.target.call(this, values, parent) : values;
50800         parent = t.target ? values : parent;
50801         if(t.target && vs instanceof Array){
50802             var buf = [];
50803             for(var i = 0, len = vs.length; i < len; i++){
50804                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50805             }
50806             return buf.join('');
50807         }
50808         return t.compiled.call(this, vs, parent);
50809     },
50810
50811     compileTpl : function(tpl){
50812         var fm = Roo.util.Format;
50813         var useF = this.disableFormats !== true;
50814         var sep = Roo.isGecko ? "+" : ",";
50815         var fn = function(m, name, format, args){
50816             if(name.substr(0, 4) == 'xtpl'){
50817                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50818             }
50819             var v;
50820             if(name.indexOf('.') != -1){
50821                 v = name;
50822             }else{
50823                 v = "values['" + name + "']";
50824             }
50825             if(format && useF){
50826                 args = args ? ',' + args : "";
50827                 if(format.substr(0, 5) != "this."){
50828                     format = "fm." + format + '(';
50829                 }else{
50830                     format = 'this.call("'+ format.substr(5) + '", ';
50831                     args = ", values";
50832                 }
50833             }else{
50834                 args= ''; format = "("+v+" === undefined ? '' : ";
50835             }
50836             return "'"+ sep + format + v + args + ")"+sep+"'";
50837         };
50838         var body;
50839         // branched to use + in gecko and [].join() in others
50840         if(Roo.isGecko){
50841             body = "tpl.compiled = function(values, parent){ return '" +
50842                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50843                     "';};";
50844         }else{
50845             body = ["tpl.compiled = function(values, parent){ return ['"];
50846             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50847             body.push("'].join('');};");
50848             body = body.join('');
50849         }
50850         /** eval:var:zzzzzzz */
50851         eval(body);
50852         return this;
50853     },
50854
50855     applyTemplate : function(values){
50856         return this.master.compiled.call(this, values, {});
50857         var s = this.subs;
50858     },
50859
50860     apply : function(){
50861         return this.applyTemplate.apply(this, arguments);
50862     },
50863
50864     compile : function(){return this;}
50865 });
50866
50867 Roo.XTemplate.from = function(el){
50868     el = Roo.getDom(el);
50869     return new Roo.XTemplate(el.value || el.innerHTML);
50870 };/*
50871  * Original code for Roojs - LGPL
50872  * <script type="text/javascript">
50873  */
50874  
50875 /**
50876  * @class Roo.XComponent
50877  * A delayed Element creator...
50878  * 
50879  * Mypart.xyx = new Roo.XComponent({
50880
50881     parent : 'Mypart.xyz', // empty == document.element.!!
50882     order : '001',
50883     name : 'xxxx'
50884     region : 'xxxx'
50885     disabled : function() {} 
50886      
50887     tree : function() { // return an tree of xtype declared components
50888         var MODULE = this;
50889         return 
50890         {
50891             xtype : 'NestedLayoutPanel',
50892             // technicall
50893         }
50894      ]
50895  *})
50896  * @extends Roo.util.Observable
50897  * @constructor
50898  * @param cfg {Object} configuration of component
50899  * 
50900  */
50901 Roo.XComponent = function(cfg) {
50902     Roo.apply(this, cfg);
50903     this.addEvents({ 
50904         /**
50905              * @event built
50906              * Fires when this the componnt is built
50907              * @param {Roo.XComponent} c the component
50908              */
50909         'built' : true,
50910         /**
50911              * @event buildcomplete
50912              * Fires on the top level element when all elements have been built
50913              * @param {Roo.XComponent} c the top level component.
50914          */
50915         'buildcomplete' : true
50916         
50917     });
50918     
50919     Roo.XComponent.register(this);
50920     this.modules = false;
50921     this.el = false; // where the layout goes..
50922     
50923     
50924 }
50925 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50926     /**
50927      * @property el
50928      * The created element (with Roo.factory())
50929      * @type {Roo.Layout}
50930      */
50931     el  : false,
50932     
50933     /**
50934      * @property el
50935      * for BC  - use el in new code
50936      * @type {Roo.Layout}
50937      */
50938     panel : false,
50939     
50940     /**
50941      * @property layout
50942      * for BC  - use el in new code
50943      * @type {Roo.Layout}
50944      */
50945     layout : false,
50946     
50947      /**
50948      * @cfg {Function|boolean} disabled
50949      * If this module is disabled by some rule, return true from the funtion
50950      */
50951     disabled : false,
50952     
50953     /**
50954      * @cfg {String} parent 
50955      * Name of parent element which it get xtype added to..
50956      */
50957     parent: false,
50958     
50959     /**
50960      * @cfg {String} order
50961      * Used to set the order in which elements are created (usefull for multiple tabs)
50962      */
50963     
50964     order : false,
50965     /**
50966      * @cfg {String} name
50967      * String to display while loading.
50968      */
50969     name : false,
50970     /**
50971      * @cfg {Array} items
50972      * A single item array - the first element is the root of the tree..
50973      * It's done this way to stay compatible with the Xtype system...
50974      */
50975     items : false
50976      
50977      
50978     
50979 });
50980
50981 Roo.apply(Roo.XComponent, {
50982     
50983     /**
50984      * @property  buildCompleted
50985      * True when the builder has completed building the interface.
50986      * @type Boolean
50987      */
50988     buildCompleted : false,
50989      
50990     /**
50991      * @property  topModule
50992      * the upper most module - uses document.element as it's constructor.
50993      * @type Object
50994      */
50995      
50996     topModule  : false,
50997       
50998     /**
50999      * @property  modules
51000      * array of modules to be created by registration system.
51001      * @type Roo.XComponent
51002      */
51003     
51004     modules : [],
51005       
51006     
51007     /**
51008      * Register components to be built later.
51009      *
51010      * This solves the following issues
51011      * - Building is not done on page load, but after an authentication process has occured.
51012      * - Interface elements are registered on page load
51013      * - Parent Interface elements may not be loaded before child, so this handles that..
51014      * 
51015      *
51016      * example:
51017      * 
51018      * MyApp.register({
51019           order : '000001',
51020           module : 'Pman.Tab.projectMgr',
51021           region : 'center',
51022           parent : 'Pman.layout',
51023           disabled : false,  // or use a function..
51024         })
51025      
51026      * * @param {Object} details about module
51027      */
51028     register : function(obj) {
51029         this.modules.push(obj);
51030          
51031     },
51032     /**
51033      * convert a string to an object..
51034      * 
51035      */
51036     
51037     toObject : function(str)
51038     {
51039         if (!str || typeof(str) == 'object') {
51040             return str;
51041         }
51042         var ar = str.split('.');
51043         var rt, o;
51044         rt = ar.shift();
51045             /** eval:var:o */
51046         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51047         if (o === false) {
51048             throw "Module not found : " + str;
51049         }
51050         Roo.each(ar, function(e) {
51051             if (typeof(o[e]) == 'undefined') {
51052                 throw "Module not found : " + str;
51053             }
51054             o = o[e];
51055         });
51056         return o;
51057         
51058     },
51059     
51060     
51061     /**
51062      * move modules into their correct place in the tree..
51063      * 
51064      */
51065     preBuild : function ()
51066     {
51067         
51068         Roo.each(this.modules , function (obj)
51069         {
51070             obj.parent = this.toObject(obj.parent);
51071             
51072             if (!obj.parent) {
51073                 this.topModule = obj;
51074                 return;
51075             }
51076             
51077             if (!obj.parent.modules) {
51078                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51079                     function(o) { return o.order + '' }
51080                 );
51081             }
51082             
51083             obj.parent.modules.add(obj);
51084         }, this);
51085     },
51086     
51087      /**
51088      * make a list of modules to build.
51089      * @return {Array} list of modules. 
51090      */ 
51091     
51092     buildOrder : function()
51093     {
51094         var _this = this;
51095         var cmp = function(a,b) {   
51096             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51097         };
51098         
51099         if (!this.topModule || !this.topModule.modules) {
51100             throw "No top level modules to build";
51101         }
51102        
51103         // make a flat list in order of modules to build.
51104         var mods = [ this.topModule ];
51105         
51106         
51107         // add modules to their parents..
51108         var addMod = function(m) {
51109            // Roo.debug && Roo.log(m.modKey);
51110             
51111             mods.push(m);
51112             if (m.modules) {
51113                 m.modules.keySort('ASC',  cmp );
51114                 m.modules.each(addMod);
51115             }
51116             // not sure if this is used any more..
51117             if (m.finalize) {
51118                 m.finalize.name = m.name + " (clean up) ";
51119                 mods.push(m.finalize);
51120             }
51121             
51122         }
51123         this.topModule.modules.keySort('ASC',  cmp );
51124         this.topModule.modules.each(addMod);
51125         return mods;
51126     },
51127     
51128      /**
51129      * Build the registered modules.
51130      * @param {Object} parent element.
51131      * @param {Function} optional method to call after module has been added.
51132      * 
51133      */ 
51134    
51135     build : function() 
51136     {
51137         
51138         this.preBuild();
51139         var mods = this.buildOrder();
51140       
51141         //this.allmods = mods;
51142         //Roo.debug && Roo.log(mods);
51143         //return;
51144         if (!mods.length) { // should not happen
51145             throw "NO modules!!!";
51146         }
51147         
51148         
51149         
51150         // flash it up as modal - so we store the mask!?
51151         Roo.MessageBox.show({ title: 'loading' });
51152         Roo.MessageBox.show({
51153            title: "Please wait...",
51154            msg: "Building Interface...",
51155            width:450,
51156            progress:true,
51157            closable:false,
51158            modal: false
51159           
51160         });
51161         var total = mods.length;
51162         
51163         var _this = this;
51164         var progressRun = function() {
51165             if (!mods.length) {
51166                 Roo.debug && Roo.log('hide?');
51167                 Roo.MessageBox.hide();
51168                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51169                 return;    
51170             }
51171             
51172             var m = mods.shift();
51173             Roo.debug && Roo.log(m);
51174             if (typeof(m) == 'function') { // not sure if this is supported any more..
51175                 m.call(this);
51176                 return progressRun.defer(10, _this);
51177             } 
51178             
51179             Roo.MessageBox.updateProgress(
51180                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51181                     " of " + total + 
51182                     (m.name ? (' - ' + m.name) : '')
51183                     );
51184             
51185          
51186             
51187             var disabled = (typeof(m.disabled) == 'function') ?
51188                 m.disabled.call(m.module.disabled) : m.disabled;    
51189             
51190             
51191             if (disabled) {
51192                 return progressRun(); // we do not update the display!
51193             }
51194             
51195             if (!m.parent) {
51196                 // it's a top level one..
51197                 var layoutbase = new Ext.BorderLayout(document.body, {
51198                
51199                     center: {
51200                          titlebar: false,
51201                          autoScroll:false,
51202                          closeOnTab: true,
51203                          tabPosition: 'top',
51204                          //resizeTabs: true,
51205                          alwaysShowTabs: true,
51206                          minTabWidth: 140
51207                     }
51208                 });
51209                 var tree = m.tree();
51210                 tree.region = 'center';
51211                 m.el = layoutbase.addxtype(tree);
51212                 m.panel = m.el;
51213                 m.layout = m.panel.layout;    
51214                 return progressRun.defer(10, _this);
51215             }
51216             
51217             var tree = m.tree();
51218             tree.region = tree.region || m.region;
51219             m.el = m.parent.el.addxtype(tree);
51220             m.fireEvent('built', m);
51221             m.panel = m.el;
51222             m.layout = m.panel.layout;    
51223             progressRun.defer(10, _this); 
51224             
51225         }
51226         progressRun.defer(1, _this);
51227      
51228         
51229         
51230     }
51231      
51232    
51233     
51234     
51235 });
51236  //<script type="text/javascript">
51237
51238
51239 /**
51240  * @class Roo.Login
51241  * @extends Roo.LayoutDialog
51242  * A generic Login Dialog..... - only one needed in theory!?!?
51243  *
51244  * Fires XComponent builder on success...
51245  * 
51246  * Sends 
51247  *    username,password, lang = for login actions.
51248  *    check = 1 for periodic checking that sesion is valid.
51249  *    passwordRequest = email request password
51250  *    logout = 1 = to logout
51251  * 
51252  * Affects: (this id="????" elements)
51253  *   loading  (removed) (used to indicate application is loading)
51254  *   loading-mask (hides) (used to hide application when it's building loading)
51255  *   
51256  * 
51257  * Usage: 
51258  *    
51259  * 
51260  * Myapp.login = Roo.Login({
51261      url: xxxx,
51262    
51263      realm : 'Myapp', 
51264      
51265      
51266      method : 'POST',
51267      
51268      
51269      * 
51270  })
51271  * 
51272  * 
51273  * 
51274  **/
51275  
51276 Roo.Login = function(cfg)
51277 {
51278     this.addEvents({
51279         'refreshed' : true
51280     });
51281     
51282     Roo.apply(this,cfg);
51283     
51284     Roo.onReady(function() {
51285         this.onLoad();
51286     }, this);
51287     // call parent..
51288     
51289    
51290     Roo.Login.superclass.constructor.call(this, this);
51291     //this.addxtype(this.items[0]);
51292     
51293     
51294 }
51295
51296
51297 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51298     
51299     /**
51300      * @cfg {String} method
51301      * Method used to query for login details.
51302      */
51303     
51304     method : 'POST',
51305     /**
51306      * @cfg {String} url
51307      * URL to query login data. - eg. baseURL + '/Login.php'
51308      */
51309     url : '',
51310     
51311     /**
51312      * @property user
51313      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51314      * @type {Object} 
51315      */
51316     user : false,
51317     /**
51318      * @property checkFails
51319      * Number of times we have attempted to get authentication check, and failed.
51320      * @type {Number} 
51321      */
51322     checkFails : 0,
51323       /**
51324      * @property intervalID
51325      * The window interval that does the constant login checking.
51326      * @type {Number} 
51327      */
51328     intervalID : 0,
51329     
51330     
51331     onLoad : function() // called on page load...
51332     {
51333         // load 
51334          
51335         if (Roo.get('loading')) { // clear any loading indicator..
51336             Roo.get('loading').remove();
51337         }
51338         
51339         //this.switchLang('en'); // set the language to english..
51340        
51341         this.check({
51342             success:  function(response, opts)  {  // check successfull...
51343             
51344                 var res = this.processResponse(response);
51345                 this.checkFails =0;
51346                 if (!res.success) { // error!
51347                     this.checkFails = 5;
51348                     //console.log('call failure');
51349                     return this.failure(response,opts);
51350                 }
51351                 
51352                 if (!res.data.id) { // id=0 == login failure.
51353                     return this.show();
51354                 }
51355                 
51356                               
51357                         //console.log(success);
51358                 this.fillAuth(res.data);   
51359                 this.checkFails =0;
51360                 Roo.XComponent.build();
51361             },
51362             failure : this.show
51363         });
51364         
51365     }, 
51366     
51367     
51368     check: function(cfg) // called every so often to refresh cookie etc..
51369     {
51370         if (cfg.again) { // could be undefined..
51371             this.checkFails++;
51372         } else {
51373             this.checkFails = 0;
51374         }
51375         var _this = this;
51376         if (this.sending) {
51377             if ( this.checkFails > 4) {
51378                 Roo.MessageBox.alert("Error",  
51379                     "Error getting authentication status. - try reloading, or wait a while", function() {
51380                         _this.sending = false;
51381                     }); 
51382                 return;
51383             }
51384             cfg.again = true;
51385             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51386             return;
51387         }
51388         this.sending = true;
51389         
51390         Roo.Ajax.request({  
51391             url: this.url,
51392             params: {
51393                 getAuthUser: true
51394             },  
51395             method: this.method,
51396             success:  cfg.success || this.success,
51397             failure : cfg.failure || this.failure,
51398             scope : this,
51399             callCfg : cfg
51400               
51401         });  
51402     }, 
51403     
51404     
51405     logout: function()
51406     {
51407         window.onbeforeunload = function() { }; // false does not work for IE..
51408         this.user = false;
51409         var _this = this;
51410         
51411         Roo.Ajax.request({  
51412             url: this.url,
51413             params: {
51414                 logout: 1
51415             },  
51416             method: 'GET',
51417             failure : function() {
51418                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51419                     document.location = document.location.toString() + '?ts=' + Math.random();
51420                 });
51421                 
51422             },
51423             success : function() {
51424                 _this.user = false;
51425                 this.checkFails =0;
51426                 // fixme..
51427                 document.location = document.location.toString() + '?ts=' + Math.random();
51428             }
51429               
51430               
51431         }); 
51432     },
51433     
51434     processResponse : function (response)
51435     {
51436         var res = '';
51437         try {
51438             res = Roo.decode(response.responseText);
51439             // oops...
51440             if (typeof(res) != 'object') {
51441                 res = { success : false, errorMsg : res, errors : true };
51442             }
51443             if (typeof(res.success) == 'undefined') {
51444                 res.success = false;
51445             }
51446             
51447         } catch(e) {
51448             res = { success : false,  errorMsg : response.responseText, errors : true };
51449         }
51450         return res;
51451     },
51452     
51453     success : function(response, opts)  // check successfull...
51454     {  
51455         this.sending = false;
51456         var res = this.processResponse(response);
51457         if (!res.success) {
51458             return this.failure(response, opts);
51459         }
51460         if (!res.data || !res.data.id) {
51461             return this.failure(response,opts);
51462         }
51463         //console.log(res);
51464         this.fillAuth(res.data);
51465         
51466         this.checkFails =0;
51467         
51468     },
51469     
51470     
51471     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51472     {
51473         this.authUser = -1;
51474         this.sending = false;
51475         var res = this.processResponse(response);
51476         //console.log(res);
51477         if ( this.checkFails > 2) {
51478         
51479             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51480                 "Error getting authentication status. - try reloading"); 
51481             return;
51482         }
51483         opts.callCfg.again = true;
51484         this.check.defer(1000, this, [ opts.callCfg ]);
51485         return;  
51486     },
51487     
51488     
51489     
51490     fillAuth: function(au) {
51491         this.startAuthCheck();
51492         this.authUserId = au.id;
51493         this.authUser = au;
51494         this.lastChecked = new Date();
51495         this.fireEvent('refreshed', au);
51496         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51497         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51498         au.lang = au.lang || 'en';
51499         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51500         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51501         this.switchLang(au.lang );
51502         
51503      
51504         // open system... - -on setyp..
51505         if (this.authUserId  < 0) {
51506             Roo.MessageBox.alert("Warning", 
51507                 "This is an open system - please set up a admin user with a password.");  
51508         }
51509          
51510         //Pman.onload(); // which should do nothing if it's a re-auth result...
51511         
51512              
51513     },
51514     
51515     startAuthCheck : function() // starter for timeout checking..
51516     {
51517         if (this.intervalID) { // timer already in place...
51518             return false;
51519         }
51520         var _this = this;
51521         this.intervalID =  window.setInterval(function() {
51522               _this.check(false);
51523             }, 120000); // every 120 secs = 2mins..
51524         
51525         
51526     },
51527          
51528     
51529     switchLang : function (lang) 
51530     {
51531         _T = typeof(_T) == 'undefined' ? false : _T;
51532           if (!_T || !lang.length) {
51533             return;
51534         }
51535         
51536         if (!_T && lang != 'en') {
51537             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51538             return;
51539         }
51540         
51541         if (typeof(_T.en) == 'undefined') {
51542             _T.en = {};
51543             Roo.apply(_T.en, _T);
51544         }
51545         
51546         if (typeof(_T[lang]) == 'undefined') {
51547             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51548             return;
51549         }
51550         
51551         
51552         Roo.apply(_T, _T[lang]);
51553         // just need to set the text values for everything...
51554         var _this = this;
51555         /* this will not work ...
51556         if (this.form) { 
51557             
51558                
51559             function formLabel(name, val) {
51560                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51561             }
51562             
51563             formLabel('password', "Password"+':');
51564             formLabel('username', "Email Address"+':');
51565             formLabel('lang', "Language"+':');
51566             this.dialog.setTitle("Login");
51567             this.dialog.buttons[0].setText("Forgot Password");
51568             this.dialog.buttons[1].setText("Login");
51569         }
51570         */
51571         
51572         
51573     },
51574     
51575     
51576     title: "Login",
51577     modal: true,
51578     width:  350,
51579     //height: 230,
51580     height: 180,
51581     shadow: true,
51582     minWidth:200,
51583     minHeight:180,
51584     //proxyDrag: true,
51585     closable: false,
51586     draggable: false,
51587     collapsible: false,
51588     resizable: false,
51589     center: {  // needed??
51590         autoScroll:false,
51591         titlebar: false,
51592        // tabPosition: 'top',
51593         hideTabs: true,
51594         closeOnTab: true,
51595         alwaysShowTabs: false
51596     } ,
51597     listeners : {
51598         
51599         show  : function(dlg)
51600         {
51601             //console.log(this);
51602             this.form = this.layout.getRegion('center').activePanel.form;
51603             this.form.dialog = dlg;
51604             this.buttons[0].form = this.form;
51605             this.buttons[0].dialog = dlg;
51606             this.buttons[1].form = this.form;
51607             this.buttons[1].dialog = dlg;
51608            
51609            //this.resizeToLogo.defer(1000,this);
51610             // this is all related to resizing for logos..
51611             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51612            //// if (!sz) {
51613              //   this.resizeToLogo.defer(1000,this);
51614              //   return;
51615            // }
51616             //var w = Ext.lib.Dom.getViewWidth() - 100;
51617             //var h = Ext.lib.Dom.getViewHeight() - 100;
51618             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51619             //this.center();
51620             if (this.disabled) {
51621                 this.hide();
51622                 return;
51623             }
51624             
51625             if (this.user.id < 0) { // used for inital setup situations.
51626                 return;
51627             }
51628             
51629             if (this.intervalID) {
51630                 // remove the timer
51631                 window.clearInterval(this.intervalID);
51632                 this.intervalID = false;
51633             }
51634             
51635             
51636             if (Roo.get('loading')) {
51637                 Roo.get('loading').remove();
51638             }
51639             if (Roo.get('loading-mask')) {
51640                 Roo.get('loading-mask').hide();
51641             }
51642             
51643             //incomming._node = tnode;
51644             this.form.reset();
51645             //this.dialog.modal = !modal;
51646             //this.dialog.show();
51647             this.el.unmask(); 
51648             
51649             
51650             this.form.setValues({
51651                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51652                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51653             });
51654             
51655             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51656             if (this.form.findField('username').getValue().length > 0 ){
51657                 this.form.findField('password').focus();
51658             } else {
51659                this.form.findField('username').focus();
51660             }
51661     
51662         }
51663     },
51664     items : [
51665          {
51666        
51667             xtype : 'ContentPanel',
51668             xns : Roo,
51669             region: 'center',
51670             fitToFrame : true,
51671             
51672             items : [
51673     
51674                 {
51675                
51676                     xtype : 'Form',
51677                     xns : Roo.form,
51678                     labelWidth: 100,
51679                     style : 'margin: 10px;',
51680                     
51681                     listeners : {
51682                         actionfailed : function(f, act) {
51683                             // form can return { errors: .... }
51684                                 
51685                             //act.result.errors // invalid form element list...
51686                             //act.result.errorMsg// invalid form element list...
51687                             
51688                             this.dialog.el.unmask();
51689                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51690                                         "Login failed - communication error - try again.");
51691                                       
51692                         },
51693                         actioncomplete: function(re, act) {
51694                              
51695                             Roo.state.Manager.set(
51696                                 this.dialog.realm + '.username',  
51697                                     this.findField('username').getValue()
51698                             );
51699                             Roo.state.Manager.set(
51700                                 this.dialog.realm + '.lang',  
51701                                 this.findField('lang').getValue() 
51702                             );
51703                             
51704                             this.dialog.fillAuth(act.result.data);
51705                               
51706                             this.dialog.hide();
51707                             
51708                             if (Roo.get('loading-mask')) {
51709                                 Roo.get('loading-mask').show();
51710                             }
51711                             Roo.XComponent.build();
51712                             
51713                              
51714                             
51715                         }
51716                     },
51717                     items : [
51718                         {
51719                             xtype : 'TextField',
51720                             xns : Roo.form,
51721                             fieldLabel: "Email Address",
51722                             name: 'username',
51723                             width:200,
51724                             autoCreate : {tag: "input", type: "text", size: "20"}
51725                         },
51726                         {
51727                             xtype : 'TextField',
51728                             xns : Roo.form,
51729                             fieldLabel: "Password",
51730                             inputType: 'password',
51731                             name: 'password',
51732                             width:200,
51733                             autoCreate : {tag: "input", type: "text", size: "20"},
51734                             listeners : {
51735                                 specialkey : function(e,ev) {
51736                                     if (ev.keyCode == 13) {
51737                                         this.form.dialog.el.mask("Logging in");
51738                                         this.form.doAction('submit', {
51739                                             url: this.form.dialog.url,
51740                                             method: this.form.dialog.method
51741                                         });
51742                                     }
51743                                 }
51744                             }  
51745                         },
51746                         {
51747                             xtype : 'ComboBox',
51748                             xns : Roo.form,
51749                             fieldLabel: "Language",
51750                             name : 'langdisp',
51751                             store: {
51752                                 xtype : 'SimpleStore',
51753                                 fields: ['lang', 'ldisp'],
51754                                 data : [
51755                                     [ 'en', 'English' ],
51756                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51757                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51758                                 ]
51759                             },
51760                             
51761                             valueField : 'lang',
51762                             hiddenName:  'lang',
51763                             width: 200,
51764                             displayField:'ldisp',
51765                             typeAhead: false,
51766                             editable: false,
51767                             mode: 'local',
51768                             triggerAction: 'all',
51769                             emptyText:'Select a Language...',
51770                             selectOnFocus:true,
51771                             listeners : {
51772                                 select :  function(cb, rec, ix) {
51773                                     this.form.switchLang(rec.data.lang);
51774                                 }
51775                             }
51776                         
51777                         }
51778                     ]
51779                 }
51780                   
51781                 
51782             ]
51783         }
51784     ],
51785     buttons : [
51786         {
51787             xtype : 'Button',
51788             xns : 'Roo',
51789             text : "Forgot Password",
51790             listeners : {
51791                 click : function() {
51792                     //console.log(this);
51793                     var n = this.form.findField('username').getValue();
51794                     if (!n.length) {
51795                         Roo.MessageBox.alert("Error", "Fill in your email address");
51796                         return;
51797                     }
51798                     Roo.Ajax.request({
51799                         url: this.dialog.url,
51800                         params: {
51801                             passwordRequest: n
51802                         },
51803                         method: this.dialog.method,
51804                         success:  function(response, opts)  {  // check successfull...
51805                         
51806                             var res = this.dialog.processResponse(response);
51807                             if (!res.success) { // error!
51808                                Roo.MessageBox.alert("Error" ,
51809                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51810                                return;
51811                             }
51812                             Roo.MessageBox.alert("Notice" ,
51813                                 "Please check you email for the Password Reset message");
51814                         },
51815                         failure : function() {
51816                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51817                         }
51818                         
51819                     });
51820                 }
51821             }
51822         },
51823         {
51824             xtype : 'Button',
51825             xns : 'Roo',
51826             text : "Login",
51827             listeners : {
51828                 
51829                 click : function () {
51830                         
51831                     this.dialog.el.mask("Logging in");
51832                     this.form.doAction('submit', {
51833                             url: this.dialog.url,
51834                             method: this.dialog.method
51835                     });
51836                 }
51837             }
51838         }
51839     ]
51840   
51841   
51842 })
51843  
51844
51845
51846