roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358
359         /**
360          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
361          * @param {String} string
362          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
363          * @return {Object} A literal with members
364          */
365         urlDecode : function(string, overwrite){
366             if(!string || !string.length){
367                 return {};
368             }
369             var obj = {};
370             var pairs = string.split('&');
371             var pair, name, value;
372             for(var i = 0, len = pairs.length; i < len; i++){
373                 pair = pairs[i].split('=');
374                 name = decodeURIComponent(pair[0]);
375                 value = decodeURIComponent(pair[1]);
376                 if(overwrite !== true){
377                     if(typeof obj[name] == "undefined"){
378                         obj[name] = value;
379                     }else if(typeof obj[name] == "string"){
380                         obj[name] = [obj[name]];
381                         obj[name].push(value);
382                     }else{
383                         obj[name].push(value);
384                     }
385                 }else{
386                     obj[name] = value;
387                 }
388             }
389             return obj;
390         },
391
392         /**
393          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
394          * passed array is not really an array, your function is called once with it.
395          * The supplied function is called with (Object item, Number index, Array allItems).
396          * @param {Array/NodeList/Mixed} array
397          * @param {Function} fn
398          * @param {Object} scope
399          */
400         each : function(array, fn, scope){
401             if(typeof array.length == "undefined" || typeof array == "string"){
402                 array = [array];
403             }
404             for(var i = 0, len = array.length; i < len; i++){
405                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
406             }
407         },
408
409         // deprecated
410         combine : function(){
411             var as = arguments, l = as.length, r = [];
412             for(var i = 0; i < l; i++){
413                 var a = as[i];
414                 if(a instanceof Array){
415                     r = r.concat(a);
416                 }else if(a.length !== undefined && !a.substr){
417                     r = r.concat(Array.prototype.slice.call(a, 0));
418                 }else{
419                     r.push(a);
420                 }
421             }
422             return r;
423         },
424
425         /**
426          * Escapes the passed string for use in a regular expression
427          * @param {String} str
428          * @return {String}
429          */
430         escapeRe : function(s) {
431             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
432         },
433
434         // internal
435         callback : function(cb, scope, args, delay){
436             if(typeof cb == "function"){
437                 if(delay){
438                     cb.defer(delay, scope, args || []);
439                 }else{
440                     cb.apply(scope, args || []);
441                 }
442             }
443         },
444
445         /**
446          * Return the dom node for the passed string (id), dom node, or Roo.Element
447          * @param {String/HTMLElement/Roo.Element} el
448          * @return HTMLElement
449          */
450         getDom : function(el){
451             if(!el){
452                 return null;
453             }
454             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
455         },
456
457         /**
458         * Shorthand for {@link Roo.ComponentMgr#get}
459         * @param {String} id
460         * @return Roo.Component
461         */
462         getCmp : function(id){
463             return Roo.ComponentMgr.get(id);
464         },
465          
466         num : function(v, defaultValue){
467             if(typeof v != 'number'){
468                 return defaultValue;
469             }
470             return v;
471         },
472
473         destroy : function(){
474             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
475                 var as = a[i];
476                 if(as){
477                     if(as.dom){
478                         as.removeAllListeners();
479                         as.remove();
480                         continue;
481                     }
482                     if(typeof as.purgeListeners == 'function'){
483                         as.purgeListeners();
484                     }
485                     if(typeof as.destroy == 'function'){
486                         as.destroy();
487                     }
488                 }
489             }
490         },
491
492         // inpired by a similar function in mootools library
493         /**
494          * Returns the type of object that is passed in. If the object passed in is null or undefined it
495          * return false otherwise it returns one of the following values:<ul>
496          * <li><b>string</b>: If the object passed is a string</li>
497          * <li><b>number</b>: If the object passed is a number</li>
498          * <li><b>boolean</b>: If the object passed is a boolean value</li>
499          * <li><b>function</b>: If the object passed is a function reference</li>
500          * <li><b>object</b>: If the object passed is an object</li>
501          * <li><b>array</b>: If the object passed is an array</li>
502          * <li><b>regexp</b>: If the object passed is a regular expression</li>
503          * <li><b>element</b>: If the object passed is a DOM Element</li>
504          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
505          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
506          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
507          * @param {Mixed} object
508          * @return {String}
509          */
510         type : function(o){
511             if(o === undefined || o === null){
512                 return false;
513             }
514             if(o.htmlElement){
515                 return 'element';
516             }
517             var t = typeof o;
518             if(t == 'object' && o.nodeName) {
519                 switch(o.nodeType) {
520                     case 1: return 'element';
521                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
522                 }
523             }
524             if(t == 'object' || t == 'function') {
525                 switch(o.constructor) {
526                     case Array: return 'array';
527                     case RegExp: return 'regexp';
528                 }
529                 if(typeof o.length == 'number' && typeof o.item == 'function') {
530                     return 'nodelist';
531                 }
532             }
533             return t;
534         },
535
536         /**
537          * Returns true if the passed value is null, undefined or an empty string (optional).
538          * @param {Mixed} value The value to test
539          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
540          * @return {Boolean}
541          */
542         isEmpty : function(v, allowBlank){
543             return v === null || v === undefined || (!allowBlank ? v === '' : false);
544         },
545         
546         /** @type Boolean */
547         isOpera : isOpera,
548         /** @type Boolean */
549         isSafari : isSafari,
550         /** @type Boolean */
551         isIE : isIE,
552         /** @type Boolean */
553         isIE7 : isIE7,
554         /** @type Boolean */
555         isGecko : isGecko,
556         /** @type Boolean */
557         isBorderBox : isBorderBox,
558         /** @type Boolean */
559         isWindows : isWindows,
560         /** @type Boolean */
561         isLinux : isLinux,
562         /** @type Boolean */
563         isMac : isMac,
564
565         /**
566          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
567          * you may want to set this to true.
568          * @type Boolean
569          */
570         useShims : ((isIE && !isIE7) || (isGecko && isMac))
571     });
572
573
574 })();
575
576 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
577                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
578 /*
579  * Based on:
580  * Ext JS Library 1.1.1
581  * Copyright(c) 2006-2007, Ext JS, LLC.
582  *
583  * Originally Released Under LGPL - original licence link has changed is not relivant.
584  *
585  * Fork - LGPL
586  * <script type="text/javascript">
587  */
588
589 (function() {    
590     // wrappedn so fnCleanup is not in global scope...
591     if(Roo.isIE) {
592         function fnCleanUp() {
593             var p = Function.prototype;
594             delete p.createSequence;
595             delete p.defer;
596             delete p.createDelegate;
597             delete p.createCallback;
598             delete p.createInterceptor;
599
600             window.detachEvent("onunload", fnCleanUp);
601         }
602         window.attachEvent("onunload", fnCleanUp);
603     }
604 })();
605
606
607 /**
608  * @class Function
609  * These functions are available on every Function object (any JavaScript function).
610  */
611 Roo.apply(Function.prototype, {
612      /**
613      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
614      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
615      * Will create a function that is bound to those 2 args.
616      * @return {Function} The new function
617     */
618     createCallback : function(/*args...*/){
619         // make args available, in function below
620         var args = arguments;
621         var method = this;
622         return function() {
623             return method.apply(window, args);
624         };
625     },
626
627     /**
628      * Creates a delegate (callback) that sets the scope to obj.
629      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
630      * Will create a function that is automatically scoped to this.
631      * @param {Object} obj (optional) The object for which the scope is set
632      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
633      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
634      *                                             if a number the args are inserted at the specified position
635      * @return {Function} The new function
636      */
637     createDelegate : function(obj, args, appendArgs){
638         var method = this;
639         return function() {
640             var callArgs = args || arguments;
641             if(appendArgs === true){
642                 callArgs = Array.prototype.slice.call(arguments, 0);
643                 callArgs = callArgs.concat(args);
644             }else if(typeof appendArgs == "number"){
645                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
646                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
647                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
648             }
649             return method.apply(obj || window, callArgs);
650         };
651     },
652
653     /**
654      * Calls this function after the number of millseconds specified.
655      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
656      * @param {Object} obj (optional) The object for which the scope is set
657      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
658      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
659      *                                             if a number the args are inserted at the specified position
660      * @return {Number} The timeout id that can be used with clearTimeout
661      */
662     defer : function(millis, obj, args, appendArgs){
663         var fn = this.createDelegate(obj, args, appendArgs);
664         if(millis){
665             return setTimeout(fn, millis);
666         }
667         fn();
668         return 0;
669     },
670     /**
671      * Create a combined function call sequence of the original function + the passed function.
672      * The resulting function returns the results of the original function.
673      * The passed fcn is called with the parameters of the original function
674      * @param {Function} fcn The function to sequence
675      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
676      * @return {Function} The new function
677      */
678     createSequence : function(fcn, scope){
679         if(typeof fcn != "function"){
680             return this;
681         }
682         var method = this;
683         return function() {
684             var retval = method.apply(this || window, arguments);
685             fcn.apply(scope || this || window, arguments);
686             return retval;
687         };
688     },
689
690     /**
691      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
692      * The resulting function returns the results of the original function.
693      * The passed fcn is called with the parameters of the original function.
694      * @addon
695      * @param {Function} fcn The function to call before the original
696      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
697      * @return {Function} The new function
698      */
699     createInterceptor : function(fcn, scope){
700         if(typeof fcn != "function"){
701             return this;
702         }
703         var method = this;
704         return function() {
705             fcn.target = this;
706             fcn.method = method;
707             if(fcn.apply(scope || this || window, arguments) === false){
708                 return;
709             }
710             return method.apply(this || window, arguments);
711         };
712     }
713 });
714 /*
715  * Based on:
716  * Ext JS Library 1.1.1
717  * Copyright(c) 2006-2007, Ext JS, LLC.
718  *
719  * Originally Released Under LGPL - original licence link has changed is not relivant.
720  *
721  * Fork - LGPL
722  * <script type="text/javascript">
723  */
724
725 Roo.applyIf(String, {
726     
727     /** @scope String */
728     
729     /**
730      * Escapes the passed string for ' and \
731      * @param {String} string The string to escape
732      * @return {String} The escaped string
733      * @static
734      */
735     escape : function(string) {
736         return string.replace(/('|\\)/g, "\\$1");
737     },
738
739     /**
740      * Pads the left side of a string with a specified character.  This is especially useful
741      * for normalizing number and date strings.  Example usage:
742      * <pre><code>
743 var s = String.leftPad('123', 5, '0');
744 // s now contains the string: '00123'
745 </code></pre>
746      * @param {String} string The original string
747      * @param {Number} size The total length of the output string
748      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
749      * @return {String} The padded string
750      * @static
751      */
752     leftPad : function (val, size, ch) {
753         var result = new String(val);
754         if(ch === null || ch === undefined || ch === '') {
755             ch = " ";
756         }
757         while (result.length < size) {
758             result = ch + result;
759         }
760         return result;
761     },
762
763     /**
764      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
765      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
766      * <pre><code>
767 var cls = 'my-class', text = 'Some text';
768 var s = String.format('<div class="{0}">{1}</div>', cls, text);
769 // s now contains the string: '<div class="my-class">Some text</div>'
770 </code></pre>
771      * @param {String} string The tokenized string to be formatted
772      * @param {String} value1 The value to replace token {0}
773      * @param {String} value2 Etc...
774      * @return {String} The formatted string
775      * @static
776      */
777     format : function(format){
778         var args = Array.prototype.slice.call(arguments, 1);
779         return format.replace(/\{(\d+)\}/g, function(m, i){
780             return Roo.util.Format.htmlEncode(args[i]);
781         });
782     }
783 });
784
785 /**
786  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
787  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
788  * they are already different, the first value passed in is returned.  Note that this method returns the new value
789  * but does not change the current string.
790  * <pre><code>
791 // alternate sort directions
792 sort = sort.toggle('ASC', 'DESC');
793
794 // instead of conditional logic:
795 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
796 </code></pre>
797  * @param {String} value The value to compare to the current string
798  * @param {String} other The new value to use if the string already equals the first value passed in
799  * @return {String} The new value
800  */
801  
802 String.prototype.toggle = function(value, other){
803     return this == value ? other : value;
804 };/*
805  * Based on:
806  * Ext JS Library 1.1.1
807  * Copyright(c) 2006-2007, Ext JS, LLC.
808  *
809  * Originally Released Under LGPL - original licence link has changed is not relivant.
810  *
811  * Fork - LGPL
812  * <script type="text/javascript">
813  */
814
815  /**
816  * @class Number
817  */
818 Roo.applyIf(Number.prototype, {
819     /**
820      * Checks whether or not the current number is within a desired range.  If the number is already within the
821      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
822      * exceeded.  Note that this method returns the constrained value but does not change the current number.
823      * @param {Number} min The minimum number in the range
824      * @param {Number} max The maximum number in the range
825      * @return {Number} The constrained value if outside the range, otherwise the current value
826      */
827     constrain : function(min, max){
828         return Math.min(Math.max(this, min), max);
829     }
830 });/*
831  * Based on:
832  * Ext JS Library 1.1.1
833  * Copyright(c) 2006-2007, Ext JS, LLC.
834  *
835  * Originally Released Under LGPL - original licence link has changed is not relivant.
836  *
837  * Fork - LGPL
838  * <script type="text/javascript">
839  */
840  /**
841  * @class Array
842  */
843 Roo.applyIf(Array.prototype, {
844     /**
845      * Checks whether or not the specified object exists in the array.
846      * @param {Object} o The object to check for
847      * @return {Number} The index of o in the array (or -1 if it is not found)
848      */
849     indexOf : function(o){
850        for (var i = 0, len = this.length; i < len; i++){
851               if(this[i] == o) return i;
852        }
853            return -1;
854     },
855
856     /**
857      * Removes the specified object from the array.  If the object is not found nothing happens.
858      * @param {Object} o The object to remove
859      */
860     remove : function(o){
861        var index = this.indexOf(o);
862        if(index != -1){
863            this.splice(index, 1);
864        }
865     },
866     /**
867      * Map (JS 1.6 compatibility)
868      * @param {Function} function  to call
869      */
870     map : function(fun )
871     {
872         var len = this.length >>> 0;
873         if (typeof fun != "function")
874             throw new TypeError();
875
876         var res = new Array(len);
877         var thisp = arguments[1];
878         for (var i = 0; i < len; i++)
879         {
880             if (i in this)
881                 res[i] = fun.call(thisp, this[i], i, this);
882         }
883
884         return res;
885     }
886     
887 });
888
889
890  /*
891  * Based on:
892  * Ext JS Library 1.1.1
893  * Copyright(c) 2006-2007, Ext JS, LLC.
894  *
895  * Originally Released Under LGPL - original licence link has changed is not relivant.
896  *
897  * Fork - LGPL
898  * <script type="text/javascript">
899  */
900
901 /**
902  * @class Date
903  *
904  * The date parsing and format syntax is a subset of
905  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
906  * supported will provide results equivalent to their PHP versions.
907  *
908  * Following is the list of all currently supported formats:
909  *<pre>
910 Sample date:
911 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
912
913 Format  Output      Description
914 ------  ----------  --------------------------------------------------------------
915   d      10         Day of the month, 2 digits with leading zeros
916   D      Wed        A textual representation of a day, three letters
917   j      10         Day of the month without leading zeros
918   l      Wednesday  A full textual representation of the day of the week
919   S      th         English ordinal day of month suffix, 2 chars (use with j)
920   w      3          Numeric representation of the day of the week
921   z      9          The julian date, or day of the year (0-365)
922   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
923   F      January    A full textual representation of the month
924   m      01         Numeric representation of a month, with leading zeros
925   M      Jan        Month name abbreviation, three letters
926   n      1          Numeric representation of a month, without leading zeros
927   t      31         Number of days in the given month
928   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
929   Y      2007       A full numeric representation of a year, 4 digits
930   y      07         A two digit representation of a year
931   a      pm         Lowercase Ante meridiem and Post meridiem
932   A      PM         Uppercase Ante meridiem and Post meridiem
933   g      3          12-hour format of an hour without leading zeros
934   G      15         24-hour format of an hour without leading zeros
935   h      03         12-hour format of an hour with leading zeros
936   H      15         24-hour format of an hour with leading zeros
937   i      05         Minutes with leading zeros
938   s      01         Seconds, with leading zeros
939   O      -0600      Difference to Greenwich time (GMT) in hours
940   T      CST        Timezone setting of the machine running the code
941   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
942 </pre>
943  *
944  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
945  * <pre><code>
946 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
947 document.write(dt.format('Y-m-d'));                         //2007-01-10
948 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
949 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
950  </code></pre>
951  *
952  * Here are some standard date/time patterns that you might find helpful.  They
953  * are not part of the source of Date.js, but to use them you can simply copy this
954  * block of code into any script that is included after Date.js and they will also become
955  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
956  * <pre><code>
957 Date.patterns = {
958     ISO8601Long:"Y-m-d H:i:s",
959     ISO8601Short:"Y-m-d",
960     ShortDate: "n/j/Y",
961     LongDate: "l, F d, Y",
962     FullDateTime: "l, F d, Y g:i:s A",
963     MonthDay: "F d",
964     ShortTime: "g:i A",
965     LongTime: "g:i:s A",
966     SortableDateTime: "Y-m-d\\TH:i:s",
967     UniversalSortableDateTime: "Y-m-d H:i:sO",
968     YearMonth: "F, Y"
969 };
970 </code></pre>
971  *
972  * Example usage:
973  * <pre><code>
974 var dt = new Date();
975 document.write(dt.format(Date.patterns.ShortDate));
976  </code></pre>
977  */
978
979 /*
980  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
981  * They generate precompiled functions from date formats instead of parsing and
982  * processing the pattern every time you format a date.  These functions are available
983  * on every Date object (any javascript function).
984  *
985  * The original article and download are here:
986  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
987  *
988  */
989  
990  
991  // was in core
992 /**
993  Returns the number of milliseconds between this date and date
994  @param {Date} date (optional) Defaults to now
995  @return {Number} The diff in milliseconds
996  @member Date getElapsed
997  */
998 Date.prototype.getElapsed = function(date) {
999         return Math.abs((date || new Date()).getTime()-this.getTime());
1000 };
1001 // was in date file..
1002
1003
1004 // private
1005 Date.parseFunctions = {count:0};
1006 // private
1007 Date.parseRegexes = [];
1008 // private
1009 Date.formatFunctions = {count:0};
1010
1011 // private
1012 Date.prototype.dateFormat = function(format) {
1013     if (Date.formatFunctions[format] == null) {
1014         Date.createNewFormat(format);
1015     }
1016     var func = Date.formatFunctions[format];
1017     return this[func]();
1018 };
1019
1020
1021 /**
1022  * Formats a date given the supplied format string
1023  * @param {String} format The format string
1024  * @return {String} The formatted date
1025  * @method
1026  */
1027 Date.prototype.format = Date.prototype.dateFormat;
1028
1029 // private
1030 Date.createNewFormat = function(format) {
1031     var funcName = "format" + Date.formatFunctions.count++;
1032     Date.formatFunctions[format] = funcName;
1033     var code = "Date.prototype." + funcName + " = function(){return ";
1034     var special = false;
1035     var ch = '';
1036     for (var i = 0; i < format.length; ++i) {
1037         ch = format.charAt(i);
1038         if (!special && ch == "\\") {
1039             special = true;
1040         }
1041         else if (special) {
1042             special = false;
1043             code += "'" + String.escape(ch) + "' + ";
1044         }
1045         else {
1046             code += Date.getFormatCode(ch);
1047         }
1048     }
1049     /** eval:var:zzzzzzzzzzzzz */
1050     eval(code.substring(0, code.length - 3) + ";}");
1051 };
1052
1053 // private
1054 Date.getFormatCode = function(character) {
1055     switch (character) {
1056     case "d":
1057         return "String.leftPad(this.getDate(), 2, '0') + ";
1058     case "D":
1059         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1060     case "j":
1061         return "this.getDate() + ";
1062     case "l":
1063         return "Date.dayNames[this.getDay()] + ";
1064     case "S":
1065         return "this.getSuffix() + ";
1066     case "w":
1067         return "this.getDay() + ";
1068     case "z":
1069         return "this.getDayOfYear() + ";
1070     case "W":
1071         return "this.getWeekOfYear() + ";
1072     case "F":
1073         return "Date.monthNames[this.getMonth()] + ";
1074     case "m":
1075         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1076     case "M":
1077         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1078     case "n":
1079         return "(this.getMonth() + 1) + ";
1080     case "t":
1081         return "this.getDaysInMonth() + ";
1082     case "L":
1083         return "(this.isLeapYear() ? 1 : 0) + ";
1084     case "Y":
1085         return "this.getFullYear() + ";
1086     case "y":
1087         return "('' + this.getFullYear()).substring(2, 4) + ";
1088     case "a":
1089         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1090     case "A":
1091         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1092     case "g":
1093         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1094     case "G":
1095         return "this.getHours() + ";
1096     case "h":
1097         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1098     case "H":
1099         return "String.leftPad(this.getHours(), 2, '0') + ";
1100     case "i":
1101         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1102     case "s":
1103         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1104     case "O":
1105         return "this.getGMTOffset() + ";
1106     case "T":
1107         return "this.getTimezone() + ";
1108     case "Z":
1109         return "(this.getTimezoneOffset() * -60) + ";
1110     default:
1111         return "'" + String.escape(character) + "' + ";
1112     }
1113 };
1114
1115 /**
1116  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1117  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1118  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1119  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1120  * string or the parse operation will fail.
1121  * Example Usage:
1122 <pre><code>
1123 //dt = Fri May 25 2007 (current date)
1124 var dt = new Date();
1125
1126 //dt = Thu May 25 2006 (today's month/day in 2006)
1127 dt = Date.parseDate("2006", "Y");
1128
1129 //dt = Sun Jan 15 2006 (all date parts specified)
1130 dt = Date.parseDate("2006-1-15", "Y-m-d");
1131
1132 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1133 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1134 </code></pre>
1135  * @param {String} input The unparsed date as a string
1136  * @param {String} format The format the date is in
1137  * @return {Date} The parsed date
1138  * @static
1139  */
1140 Date.parseDate = function(input, format) {
1141     if (Date.parseFunctions[format] == null) {
1142         Date.createParser(format);
1143     }
1144     var func = Date.parseFunctions[format];
1145     return Date[func](input);
1146 };
1147 /**
1148  * @private
1149  */
1150 Date.createParser = function(format) {
1151     var funcName = "parse" + Date.parseFunctions.count++;
1152     var regexNum = Date.parseRegexes.length;
1153     var currentGroup = 1;
1154     Date.parseFunctions[format] = funcName;
1155
1156     var code = "Date." + funcName + " = function(input){\n"
1157         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1158         + "var d = new Date();\n"
1159         + "y = d.getFullYear();\n"
1160         + "m = d.getMonth();\n"
1161         + "d = d.getDate();\n"
1162         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1163         + "if (results && results.length > 0) {";
1164     var regex = "";
1165
1166     var special = false;
1167     var ch = '';
1168     for (var i = 0; i < format.length; ++i) {
1169         ch = format.charAt(i);
1170         if (!special && ch == "\\") {
1171             special = true;
1172         }
1173         else if (special) {
1174             special = false;
1175             regex += String.escape(ch);
1176         }
1177         else {
1178             var obj = Date.formatCodeToRegex(ch, currentGroup);
1179             currentGroup += obj.g;
1180             regex += obj.s;
1181             if (obj.g && obj.c) {
1182                 code += obj.c;
1183             }
1184         }
1185     }
1186
1187     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1188         + "{v = new Date(y, m, d, h, i, s);}\n"
1189         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1190         + "{v = new Date(y, m, d, h, i);}\n"
1191         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1192         + "{v = new Date(y, m, d, h);}\n"
1193         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1194         + "{v = new Date(y, m, d);}\n"
1195         + "else if (y >= 0 && m >= 0)\n"
1196         + "{v = new Date(y, m);}\n"
1197         + "else if (y >= 0)\n"
1198         + "{v = new Date(y);}\n"
1199         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1200         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1201         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1202         + ";}";
1203
1204     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1205     /** eval:var:zzzzzzzzzzzzz */
1206     eval(code);
1207 };
1208
1209 // private
1210 Date.formatCodeToRegex = function(character, currentGroup) {
1211     switch (character) {
1212     case "D":
1213         return {g:0,
1214         c:null,
1215         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1216     case "j":
1217         return {g:1,
1218             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1219             s:"(\\d{1,2})"}; // day of month without leading zeroes
1220     case "d":
1221         return {g:1,
1222             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1223             s:"(\\d{2})"}; // day of month with leading zeroes
1224     case "l":
1225         return {g:0,
1226             c:null,
1227             s:"(?:" + Date.dayNames.join("|") + ")"};
1228     case "S":
1229         return {g:0,
1230             c:null,
1231             s:"(?:st|nd|rd|th)"};
1232     case "w":
1233         return {g:0,
1234             c:null,
1235             s:"\\d"};
1236     case "z":
1237         return {g:0,
1238             c:null,
1239             s:"(?:\\d{1,3})"};
1240     case "W":
1241         return {g:0,
1242             c:null,
1243             s:"(?:\\d{2})"};
1244     case "F":
1245         return {g:1,
1246             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1247             s:"(" + Date.monthNames.join("|") + ")"};
1248     case "M":
1249         return {g:1,
1250             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1251             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1252     case "n":
1253         return {g:1,
1254             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1255             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1256     case "m":
1257         return {g:1,
1258             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1259             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1260     case "t":
1261         return {g:0,
1262             c:null,
1263             s:"\\d{1,2}"};
1264     case "L":
1265         return {g:0,
1266             c:null,
1267             s:"(?:1|0)"};
1268     case "Y":
1269         return {g:1,
1270             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1271             s:"(\\d{4})"};
1272     case "y":
1273         return {g:1,
1274             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1275                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1276             s:"(\\d{1,2})"};
1277     case "a":
1278         return {g:1,
1279             c:"if (results[" + currentGroup + "] == 'am') {\n"
1280                 + "if (h == 12) { h = 0; }\n"
1281                 + "} else { if (h < 12) { h += 12; }}",
1282             s:"(am|pm)"};
1283     case "A":
1284         return {g:1,
1285             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1286                 + "if (h == 12) { h = 0; }\n"
1287                 + "} else { if (h < 12) { h += 12; }}",
1288             s:"(AM|PM)"};
1289     case "g":
1290     case "G":
1291         return {g:1,
1292             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1294     case "h":
1295     case "H":
1296         return {g:1,
1297             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1299     case "i":
1300         return {g:1,
1301             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"};
1303     case "s":
1304         return {g:1,
1305             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1306             s:"(\\d{2})"};
1307     case "O":
1308         return {g:1,
1309             c:[
1310                 "o = results[", currentGroup, "];\n",
1311                 "var sn = o.substring(0,1);\n", // get + / - sign
1312                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1313                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1314                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1315                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1316             ].join(""),
1317             s:"([+\-]\\d{4})"};
1318     case "T":
1319         return {g:0,
1320             c:null,
1321             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1322     case "Z":
1323         return {g:1,
1324             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1325                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1326             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1327     default:
1328         return {g:0,
1329             c:null,
1330             s:String.escape(character)};
1331     }
1332 };
1333
1334 /**
1335  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1336  * @return {String} The abbreviated timezone name (e.g. 'CST')
1337  */
1338 Date.prototype.getTimezone = function() {
1339     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1340 };
1341
1342 /**
1343  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1344  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1345  */
1346 Date.prototype.getGMTOffset = function() {
1347     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1348         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1349         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1350 };
1351
1352 /**
1353  * Get the numeric day number of the year, adjusted for leap year.
1354  * @return {Number} 0 through 364 (365 in leap years)
1355  */
1356 Date.prototype.getDayOfYear = function() {
1357     var num = 0;
1358     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1359     for (var i = 0; i < this.getMonth(); ++i) {
1360         num += Date.daysInMonth[i];
1361     }
1362     return num + this.getDate() - 1;
1363 };
1364
1365 /**
1366  * Get the string representation of the numeric week number of the year
1367  * (equivalent to the format specifier 'W').
1368  * @return {String} '00' through '52'
1369  */
1370 Date.prototype.getWeekOfYear = function() {
1371     // Skip to Thursday of this week
1372     var now = this.getDayOfYear() + (4 - this.getDay());
1373     // Find the first Thursday of the year
1374     var jan1 = new Date(this.getFullYear(), 0, 1);
1375     var then = (7 - jan1.getDay() + 4);
1376     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1377 };
1378
1379 /**
1380  * Whether or not the current date is in a leap year.
1381  * @return {Boolean} True if the current date is in a leap year, else false
1382  */
1383 Date.prototype.isLeapYear = function() {
1384     var year = this.getFullYear();
1385     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1386 };
1387
1388 /**
1389  * Get the first day of the current month, adjusted for leap year.  The returned value
1390  * is the numeric day index within the week (0-6) which can be used in conjunction with
1391  * the {@link #monthNames} array to retrieve the textual day name.
1392  * Example:
1393  *<pre><code>
1394 var dt = new Date('1/10/2007');
1395 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1396 </code></pre>
1397  * @return {Number} The day number (0-6)
1398  */
1399 Date.prototype.getFirstDayOfMonth = function() {
1400     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1401     return (day < 0) ? (day + 7) : day;
1402 };
1403
1404 /**
1405  * Get the last day of the current month, adjusted for leap year.  The returned value
1406  * is the numeric day index within the week (0-6) which can be used in conjunction with
1407  * the {@link #monthNames} array to retrieve the textual day name.
1408  * Example:
1409  *<pre><code>
1410 var dt = new Date('1/10/2007');
1411 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1412 </code></pre>
1413  * @return {Number} The day number (0-6)
1414  */
1415 Date.prototype.getLastDayOfMonth = function() {
1416     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1417     return (day < 0) ? (day + 7) : day;
1418 };
1419
1420
1421 /**
1422  * Get the first date of this date's month
1423  * @return {Date}
1424  */
1425 Date.prototype.getFirstDateOfMonth = function() {
1426     return new Date(this.getFullYear(), this.getMonth(), 1);
1427 };
1428
1429 /**
1430  * Get the last date of this date's month
1431  * @return {Date}
1432  */
1433 Date.prototype.getLastDateOfMonth = function() {
1434     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1435 };
1436 /**
1437  * Get the number of days in the current month, adjusted for leap year.
1438  * @return {Number} The number of days in the month
1439  */
1440 Date.prototype.getDaysInMonth = function() {
1441     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1442     return Date.daysInMonth[this.getMonth()];
1443 };
1444
1445 /**
1446  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1447  * @return {String} 'st, 'nd', 'rd' or 'th'
1448  */
1449 Date.prototype.getSuffix = function() {
1450     switch (this.getDate()) {
1451         case 1:
1452         case 21:
1453         case 31:
1454             return "st";
1455         case 2:
1456         case 22:
1457             return "nd";
1458         case 3:
1459         case 23:
1460             return "rd";
1461         default:
1462             return "th";
1463     }
1464 };
1465
1466 // private
1467 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1468
1469 /**
1470  * An array of textual month names.
1471  * Override these values for international dates, for example...
1472  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1473  * @type Array
1474  * @static
1475  */
1476 Date.monthNames =
1477    ["January",
1478     "February",
1479     "March",
1480     "April",
1481     "May",
1482     "June",
1483     "July",
1484     "August",
1485     "September",
1486     "October",
1487     "November",
1488     "December"];
1489
1490 /**
1491  * An array of textual day names.
1492  * Override these values for international dates, for example...
1493  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1494  * @type Array
1495  * @static
1496  */
1497 Date.dayNames =
1498    ["Sunday",
1499     "Monday",
1500     "Tuesday",
1501     "Wednesday",
1502     "Thursday",
1503     "Friday",
1504     "Saturday"];
1505
1506 // private
1507 Date.y2kYear = 50;
1508 // private
1509 Date.monthNumbers = {
1510     Jan:0,
1511     Feb:1,
1512     Mar:2,
1513     Apr:3,
1514     May:4,
1515     Jun:5,
1516     Jul:6,
1517     Aug:7,
1518     Sep:8,
1519     Oct:9,
1520     Nov:10,
1521     Dec:11};
1522
1523 /**
1524  * Creates and returns a new Date instance with the exact same date value as the called instance.
1525  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1526  * variable will also be changed.  When the intention is to create a new variable that will not
1527  * modify the original instance, you should create a clone.
1528  *
1529  * Example of correctly cloning a date:
1530  * <pre><code>
1531 //wrong way:
1532 var orig = new Date('10/1/2006');
1533 var copy = orig;
1534 copy.setDate(5);
1535 document.write(orig);  //returns 'Thu Oct 05 2006'!
1536
1537 //correct way:
1538 var orig = new Date('10/1/2006');
1539 var copy = orig.clone();
1540 copy.setDate(5);
1541 document.write(orig);  //returns 'Thu Oct 01 2006'
1542 </code></pre>
1543  * @return {Date} The new Date instance
1544  */
1545 Date.prototype.clone = function() {
1546         return new Date(this.getTime());
1547 };
1548
1549 /**
1550  * Clears any time information from this date
1551  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1552  @return {Date} this or the clone
1553  */
1554 Date.prototype.clearTime = function(clone){
1555     if(clone){
1556         return this.clone().clearTime();
1557     }
1558     this.setHours(0);
1559     this.setMinutes(0);
1560     this.setSeconds(0);
1561     this.setMilliseconds(0);
1562     return this;
1563 };
1564
1565 // private
1566 // safari setMonth is broken
1567 if(Roo.isSafari){
1568     Date.brokenSetMonth = Date.prototype.setMonth;
1569         Date.prototype.setMonth = function(num){
1570                 if(num <= -1){
1571                         var n = Math.ceil(-num);
1572                         var back_year = Math.ceil(n/12);
1573                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1574                         this.setFullYear(this.getFullYear() - back_year);
1575                         return Date.brokenSetMonth.call(this, month);
1576                 } else {
1577                         return Date.brokenSetMonth.apply(this, arguments);
1578                 }
1579         };
1580 }
1581
1582 /** Date interval constant 
1583 * @static 
1584 * @type String */
1585 Date.MILLI = "ms";
1586 /** Date interval constant 
1587 * @static 
1588 * @type String */
1589 Date.SECOND = "s";
1590 /** Date interval constant 
1591 * @static 
1592 * @type String */
1593 Date.MINUTE = "mi";
1594 /** Date interval constant 
1595 * @static 
1596 * @type String */
1597 Date.HOUR = "h";
1598 /** Date interval constant 
1599 * @static 
1600 * @type String */
1601 Date.DAY = "d";
1602 /** Date interval constant 
1603 * @static 
1604 * @type String */
1605 Date.MONTH = "mo";
1606 /** Date interval constant 
1607 * @static 
1608 * @type String */
1609 Date.YEAR = "y";
1610
1611 /**
1612  * Provides a convenient method of performing basic date arithmetic.  This method
1613  * does not modify the Date instance being called - it creates and returns
1614  * a new Date instance containing the resulting date value.
1615  *
1616  * Examples:
1617  * <pre><code>
1618 //Basic usage:
1619 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1620 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1621
1622 //Negative values will subtract correctly:
1623 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1624 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1625
1626 //You can even chain several calls together in one line!
1627 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1628 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1629  </code></pre>
1630  *
1631  * @param {String} interval   A valid date interval enum value
1632  * @param {Number} value      The amount to add to the current date
1633  * @return {Date} The new Date instance
1634  */
1635 Date.prototype.add = function(interval, value){
1636   var d = this.clone();
1637   if (!interval || value === 0) return d;
1638   switch(interval.toLowerCase()){
1639     case Date.MILLI:
1640       d.setMilliseconds(this.getMilliseconds() + value);
1641       break;
1642     case Date.SECOND:
1643       d.setSeconds(this.getSeconds() + value);
1644       break;
1645     case Date.MINUTE:
1646       d.setMinutes(this.getMinutes() + value);
1647       break;
1648     case Date.HOUR:
1649       d.setHours(this.getHours() + value);
1650       break;
1651     case Date.DAY:
1652       d.setDate(this.getDate() + value);
1653       break;
1654     case Date.MONTH:
1655       var day = this.getDate();
1656       if(day > 28){
1657           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1658       }
1659       d.setDate(day);
1660       d.setMonth(this.getMonth() + value);
1661       break;
1662     case Date.YEAR:
1663       d.setFullYear(this.getFullYear() + value);
1664       break;
1665   }
1666   return d;
1667 };/*
1668  * Based on:
1669  * Ext JS Library 1.1.1
1670  * Copyright(c) 2006-2007, Ext JS, LLC.
1671  *
1672  * Originally Released Under LGPL - original licence link has changed is not relivant.
1673  *
1674  * Fork - LGPL
1675  * <script type="text/javascript">
1676  */
1677
1678 Roo.lib.Dom = {
1679     getViewWidth : function(full) {
1680         return full ? this.getDocumentWidth() : this.getViewportWidth();
1681     },
1682
1683     getViewHeight : function(full) {
1684         return full ? this.getDocumentHeight() : this.getViewportHeight();
1685     },
1686
1687     getDocumentHeight: function() {
1688         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1689         return Math.max(scrollHeight, this.getViewportHeight());
1690     },
1691
1692     getDocumentWidth: function() {
1693         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1694         return Math.max(scrollWidth, this.getViewportWidth());
1695     },
1696
1697     getViewportHeight: function() {
1698         var height = self.innerHeight;
1699         var mode = document.compatMode;
1700
1701         if ((mode || Roo.isIE) && !Roo.isOpera) {
1702             height = (mode == "CSS1Compat") ?
1703                      document.documentElement.clientHeight :
1704                      document.body.clientHeight;
1705         }
1706
1707         return height;
1708     },
1709
1710     getViewportWidth: function() {
1711         var width = self.innerWidth;
1712         var mode = document.compatMode;
1713
1714         if (mode || Roo.isIE) {
1715             width = (mode == "CSS1Compat") ?
1716                     document.documentElement.clientWidth :
1717                     document.body.clientWidth;
1718         }
1719         return width;
1720     },
1721
1722     isAncestor : function(p, c) {
1723         p = Roo.getDom(p);
1724         c = Roo.getDom(c);
1725         if (!p || !c) {
1726             return false;
1727         }
1728
1729         if (p.contains && !Roo.isSafari) {
1730             return p.contains(c);
1731         } else if (p.compareDocumentPosition) {
1732             return !!(p.compareDocumentPosition(c) & 16);
1733         } else {
1734             var parent = c.parentNode;
1735             while (parent) {
1736                 if (parent == p) {
1737                     return true;
1738                 }
1739                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1740                     return false;
1741                 }
1742                 parent = parent.parentNode;
1743             }
1744             return false;
1745         }
1746     },
1747
1748     getRegion : function(el) {
1749         return Roo.lib.Region.getRegion(el);
1750     },
1751
1752     getY : function(el) {
1753         return this.getXY(el)[1];
1754     },
1755
1756     getX : function(el) {
1757         return this.getXY(el)[0];
1758     },
1759
1760     getXY : function(el) {
1761         var p, pe, b, scroll, bd = document.body;
1762         el = Roo.getDom(el);
1763         var fly = Roo.lib.AnimBase.fly;
1764         if (el.getBoundingClientRect) {
1765             b = el.getBoundingClientRect();
1766             scroll = fly(document).getScroll();
1767             return [b.left + scroll.left, b.top + scroll.top];
1768         }
1769         var x = 0, y = 0;
1770
1771         p = el;
1772
1773         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1774
1775         while (p) {
1776
1777             x += p.offsetLeft;
1778             y += p.offsetTop;
1779
1780             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1781                 hasAbsolute = true;
1782             }
1783
1784             if (Roo.isGecko) {
1785                 pe = fly(p);
1786
1787                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1788                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1789
1790
1791                 x += bl;
1792                 y += bt;
1793
1794
1795                 if (p != el && pe.getStyle('overflow') != 'visible') {
1796                     x += bl;
1797                     y += bt;
1798                 }
1799             }
1800             p = p.offsetParent;
1801         }
1802
1803         if (Roo.isSafari && hasAbsolute) {
1804             x -= bd.offsetLeft;
1805             y -= bd.offsetTop;
1806         }
1807
1808         if (Roo.isGecko && !hasAbsolute) {
1809             var dbd = fly(bd);
1810             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1811             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1812         }
1813
1814         p = el.parentNode;
1815         while (p && p != bd) {
1816             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1817                 x -= p.scrollLeft;
1818                 y -= p.scrollTop;
1819             }
1820             p = p.parentNode;
1821         }
1822         return [x, y];
1823     },
1824  
1825   
1826
1827
1828     setXY : function(el, xy) {
1829         el = Roo.fly(el, '_setXY');
1830         el.position();
1831         var pts = el.translatePoints(xy);
1832         if (xy[0] !== false) {
1833             el.dom.style.left = pts.left + "px";
1834         }
1835         if (xy[1] !== false) {
1836             el.dom.style.top = pts.top + "px";
1837         }
1838     },
1839
1840     setX : function(el, x) {
1841         this.setXY(el, [x, false]);
1842     },
1843
1844     setY : function(el, y) {
1845         this.setXY(el, [false, y]);
1846     }
1847 };
1848 /*
1849  * Portions of this file are based on pieces of Yahoo User Interface Library
1850  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1851  * YUI licensed under the BSD License:
1852  * http://developer.yahoo.net/yui/license.txt
1853  * <script type="text/javascript">
1854  *
1855  */
1856
1857 Roo.lib.Event = function() {
1858     var loadComplete = false;
1859     var listeners = [];
1860     var unloadListeners = [];
1861     var retryCount = 0;
1862     var onAvailStack = [];
1863     var counter = 0;
1864     var lastError = null;
1865
1866     return {
1867         POLL_RETRYS: 200,
1868         POLL_INTERVAL: 20,
1869         EL: 0,
1870         TYPE: 1,
1871         FN: 2,
1872         WFN: 3,
1873         OBJ: 3,
1874         ADJ_SCOPE: 4,
1875         _interval: null,
1876
1877         startInterval: function() {
1878             if (!this._interval) {
1879                 var self = this;
1880                 var callback = function() {
1881                     self._tryPreloadAttach();
1882                 };
1883                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1884
1885             }
1886         },
1887
1888         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1889             onAvailStack.push({ id:         p_id,
1890                 fn:         p_fn,
1891                 obj:        p_obj,
1892                 override:   p_override,
1893                 checkReady: false    });
1894
1895             retryCount = this.POLL_RETRYS;
1896             this.startInterval();
1897         },
1898
1899
1900         addListener: function(el, eventName, fn) {
1901             el = Roo.getDom(el);
1902             if (!el || !fn) {
1903                 return false;
1904             }
1905
1906             if ("unload" == eventName) {
1907                 unloadListeners[unloadListeners.length] =
1908                 [el, eventName, fn];
1909                 return true;
1910             }
1911
1912             var wrappedFn = function(e) {
1913                 return fn(Roo.lib.Event.getEvent(e));
1914             };
1915
1916             var li = [el, eventName, fn, wrappedFn];
1917
1918             var index = listeners.length;
1919             listeners[index] = li;
1920
1921             this.doAdd(el, eventName, wrappedFn, false);
1922             return true;
1923
1924         },
1925
1926
1927         removeListener: function(el, eventName, fn) {
1928             var i, len;
1929
1930             el = Roo.getDom(el);
1931
1932             if(!fn) {
1933                 return this.purgeElement(el, false, eventName);
1934             }
1935
1936
1937             if ("unload" == eventName) {
1938
1939                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1940                     var li = unloadListeners[i];
1941                     if (li &&
1942                         li[0] == el &&
1943                         li[1] == eventName &&
1944                         li[2] == fn) {
1945                         unloadListeners.splice(i, 1);
1946                         return true;
1947                     }
1948                 }
1949
1950                 return false;
1951             }
1952
1953             var cacheItem = null;
1954
1955
1956             var index = arguments[3];
1957
1958             if ("undefined" == typeof index) {
1959                 index = this._getCacheIndex(el, eventName, fn);
1960             }
1961
1962             if (index >= 0) {
1963                 cacheItem = listeners[index];
1964             }
1965
1966             if (!el || !cacheItem) {
1967                 return false;
1968             }
1969
1970             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1971
1972             delete listeners[index][this.WFN];
1973             delete listeners[index][this.FN];
1974             listeners.splice(index, 1);
1975
1976             return true;
1977
1978         },
1979
1980
1981         getTarget: function(ev, resolveTextNode) {
1982             ev = ev.browserEvent || ev;
1983             var t = ev.target || ev.srcElement;
1984             return this.resolveTextNode(t);
1985         },
1986
1987
1988         resolveTextNode: function(node) {
1989             if (Roo.isSafari && node && 3 == node.nodeType) {
1990                 return node.parentNode;
1991             } else {
1992                 return node;
1993             }
1994         },
1995
1996
1997         getPageX: function(ev) {
1998             ev = ev.browserEvent || ev;
1999             var x = ev.pageX;
2000             if (!x && 0 !== x) {
2001                 x = ev.clientX || 0;
2002
2003                 if (Roo.isIE) {
2004                     x += this.getScroll()[1];
2005                 }
2006             }
2007
2008             return x;
2009         },
2010
2011
2012         getPageY: function(ev) {
2013             ev = ev.browserEvent || ev;
2014             var y = ev.pageY;
2015             if (!y && 0 !== y) {
2016                 y = ev.clientY || 0;
2017
2018                 if (Roo.isIE) {
2019                     y += this.getScroll()[0];
2020                 }
2021             }
2022
2023
2024             return y;
2025         },
2026
2027
2028         getXY: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             return [this.getPageX(ev), this.getPageY(ev)];
2031         },
2032
2033
2034         getRelatedTarget: function(ev) {
2035             ev = ev.browserEvent || ev;
2036             var t = ev.relatedTarget;
2037             if (!t) {
2038                 if (ev.type == "mouseout") {
2039                     t = ev.toElement;
2040                 } else if (ev.type == "mouseover") {
2041                     t = ev.fromElement;
2042                 }
2043             }
2044
2045             return this.resolveTextNode(t);
2046         },
2047
2048
2049         getTime: function(ev) {
2050             ev = ev.browserEvent || ev;
2051             if (!ev.time) {
2052                 var t = new Date().getTime();
2053                 try {
2054                     ev.time = t;
2055                 } catch(ex) {
2056                     this.lastError = ex;
2057                     return t;
2058                 }
2059             }
2060
2061             return ev.time;
2062         },
2063
2064
2065         stopEvent: function(ev) {
2066             this.stopPropagation(ev);
2067             this.preventDefault(ev);
2068         },
2069
2070
2071         stopPropagation: function(ev) {
2072             ev = ev.browserEvent || ev;
2073             if (ev.stopPropagation) {
2074                 ev.stopPropagation();
2075             } else {
2076                 ev.cancelBubble = true;
2077             }
2078         },
2079
2080
2081         preventDefault: function(ev) {
2082             ev = ev.browserEvent || ev;
2083             if(ev.preventDefault) {
2084                 ev.preventDefault();
2085             } else {
2086                 ev.returnValue = false;
2087             }
2088         },
2089
2090
2091         getEvent: function(e) {
2092             var ev = e || window.event;
2093             if (!ev) {
2094                 var c = this.getEvent.caller;
2095                 while (c) {
2096                     ev = c.arguments[0];
2097                     if (ev && Event == ev.constructor) {
2098                         break;
2099                     }
2100                     c = c.caller;
2101                 }
2102             }
2103             return ev;
2104         },
2105
2106
2107         getCharCode: function(ev) {
2108             ev = ev.browserEvent || ev;
2109             return ev.charCode || ev.keyCode || 0;
2110         },
2111
2112
2113         _getCacheIndex: function(el, eventName, fn) {
2114             for (var i = 0,len = listeners.length; i < len; ++i) {
2115                 var li = listeners[i];
2116                 if (li &&
2117                     li[this.FN] == fn &&
2118                     li[this.EL] == el &&
2119                     li[this.TYPE] == eventName) {
2120                     return i;
2121                 }
2122             }
2123
2124             return -1;
2125         },
2126
2127
2128         elCache: {},
2129
2130
2131         getEl: function(id) {
2132             return document.getElementById(id);
2133         },
2134
2135
2136         clearCache: function() {
2137         },
2138
2139
2140         _load: function(e) {
2141             loadComplete = true;
2142             var EU = Roo.lib.Event;
2143
2144
2145             if (Roo.isIE) {
2146                 EU.doRemove(window, "load", EU._load);
2147             }
2148         },
2149
2150
2151         _tryPreloadAttach: function() {
2152
2153             if (this.locked) {
2154                 return false;
2155             }
2156
2157             this.locked = true;
2158
2159
2160             var tryAgain = !loadComplete;
2161             if (!tryAgain) {
2162                 tryAgain = (retryCount > 0);
2163             }
2164
2165
2166             var notAvail = [];
2167             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2168                 var item = onAvailStack[i];
2169                 if (item) {
2170                     var el = this.getEl(item.id);
2171
2172                     if (el) {
2173                         if (!item.checkReady ||
2174                             loadComplete ||
2175                             el.nextSibling ||
2176                             (document && document.body)) {
2177
2178                             var scope = el;
2179                             if (item.override) {
2180                                 if (item.override === true) {
2181                                     scope = item.obj;
2182                                 } else {
2183                                     scope = item.override;
2184                                 }
2185                             }
2186                             item.fn.call(scope, item.obj);
2187                             onAvailStack[i] = null;
2188                         }
2189                     } else {
2190                         notAvail.push(item);
2191                     }
2192                 }
2193             }
2194
2195             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2196
2197             if (tryAgain) {
2198
2199                 this.startInterval();
2200             } else {
2201                 clearInterval(this._interval);
2202                 this._interval = null;
2203             }
2204
2205             this.locked = false;
2206
2207             return true;
2208
2209         },
2210
2211
2212         purgeElement: function(el, recurse, eventName) {
2213             var elListeners = this.getListeners(el, eventName);
2214             if (elListeners) {
2215                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2216                     var l = elListeners[i];
2217                     this.removeListener(el, l.type, l.fn);
2218                 }
2219             }
2220
2221             if (recurse && el && el.childNodes) {
2222                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2223                     this.purgeElement(el.childNodes[i], recurse, eventName);
2224                 }
2225             }
2226         },
2227
2228
2229         getListeners: function(el, eventName) {
2230             var results = [], searchLists;
2231             if (!eventName) {
2232                 searchLists = [listeners, unloadListeners];
2233             } else if (eventName == "unload") {
2234                 searchLists = [unloadListeners];
2235             } else {
2236                 searchLists = [listeners];
2237             }
2238
2239             for (var j = 0; j < searchLists.length; ++j) {
2240                 var searchList = searchLists[j];
2241                 if (searchList && searchList.length > 0) {
2242                     for (var i = 0,len = searchList.length; i < len; ++i) {
2243                         var l = searchList[i];
2244                         if (l && l[this.EL] === el &&
2245                             (!eventName || eventName === l[this.TYPE])) {
2246                             results.push({
2247                                 type:   l[this.TYPE],
2248                                 fn:     l[this.FN],
2249                                 obj:    l[this.OBJ],
2250                                 adjust: l[this.ADJ_SCOPE],
2251                                 index:  i
2252                             });
2253                         }
2254                     }
2255                 }
2256             }
2257
2258             return (results.length) ? results : null;
2259         },
2260
2261
2262         _unload: function(e) {
2263
2264             var EU = Roo.lib.Event, i, j, l, len, index;
2265
2266             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2267                 l = unloadListeners[i];
2268                 if (l) {
2269                     var scope = window;
2270                     if (l[EU.ADJ_SCOPE]) {
2271                         if (l[EU.ADJ_SCOPE] === true) {
2272                             scope = l[EU.OBJ];
2273                         } else {
2274                             scope = l[EU.ADJ_SCOPE];
2275                         }
2276                     }
2277                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2278                     unloadListeners[i] = null;
2279                     l = null;
2280                     scope = null;
2281                 }
2282             }
2283
2284             unloadListeners = null;
2285
2286             if (listeners && listeners.length > 0) {
2287                 j = listeners.length;
2288                 while (j) {
2289                     index = j - 1;
2290                     l = listeners[index];
2291                     if (l) {
2292                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2293                                 l[EU.FN], index);
2294                     }
2295                     j = j - 1;
2296                 }
2297                 l = null;
2298
2299                 EU.clearCache();
2300             }
2301
2302             EU.doRemove(window, "unload", EU._unload);
2303
2304         },
2305
2306
2307         getScroll: function() {
2308             var dd = document.documentElement, db = document.body;
2309             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2310                 return [dd.scrollTop, dd.scrollLeft];
2311             } else if (db) {
2312                 return [db.scrollTop, db.scrollLeft];
2313             } else {
2314                 return [0, 0];
2315             }
2316         },
2317
2318
2319         doAdd: function () {
2320             if (window.addEventListener) {
2321                 return function(el, eventName, fn, capture) {
2322                     el.addEventListener(eventName, fn, (capture));
2323                 };
2324             } else if (window.attachEvent) {
2325                 return function(el, eventName, fn, capture) {
2326                     el.attachEvent("on" + eventName, fn);
2327                 };
2328             } else {
2329                 return function() {
2330                 };
2331             }
2332         }(),
2333
2334
2335         doRemove: function() {
2336             if (window.removeEventListener) {
2337                 return function (el, eventName, fn, capture) {
2338                     el.removeEventListener(eventName, fn, (capture));
2339                 };
2340             } else if (window.detachEvent) {
2341                 return function (el, eventName, fn) {
2342                     el.detachEvent("on" + eventName, fn);
2343                 };
2344             } else {
2345                 return function() {
2346                 };
2347             }
2348         }()
2349     };
2350     
2351 }();
2352 (function() {     
2353    
2354     var E = Roo.lib.Event;
2355     E.on = E.addListener;
2356     E.un = E.removeListener;
2357
2358     if (document && document.body) {
2359         E._load();
2360     } else {
2361         E.doAdd(window, "load", E._load);
2362     }
2363     E.doAdd(window, "unload", E._unload);
2364     E._tryPreloadAttach();
2365 })();
2366
2367 /*
2368  * Portions of this file are based on pieces of Yahoo User Interface Library
2369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2370  * YUI licensed under the BSD License:
2371  * http://developer.yahoo.net/yui/license.txt
2372  * <script type="text/javascript">
2373  *
2374  */
2375
2376 (function() {
2377     
2378     Roo.lib.Ajax = {
2379         request : function(method, uri, cb, data, options) {
2380             if(options){
2381                 var hs = options.headers;
2382                 if(hs){
2383                     for(var h in hs){
2384                         if(hs.hasOwnProperty(h)){
2385                             this.initHeader(h, hs[h], false);
2386                         }
2387                     }
2388                 }
2389                 if(options.xmlData){
2390                     this.initHeader('Content-Type', 'text/xml', false);
2391                     method = 'POST';
2392                     data = options.xmlData;
2393                 }
2394             }
2395
2396             return this.asyncRequest(method, uri, cb, data);
2397         },
2398
2399         serializeForm : function(form) {
2400             if(typeof form == 'string') {
2401                 form = (document.getElementById(form) || document.forms[form]);
2402             }
2403
2404             var el, name, val, disabled, data = '', hasSubmit = false;
2405             for (var i = 0; i < form.elements.length; i++) {
2406                 el = form.elements[i];
2407                 disabled = form.elements[i].disabled;
2408                 name = form.elements[i].name;
2409                 val = form.elements[i].value;
2410
2411                 if (!disabled && name){
2412                     switch (el.type)
2413                             {
2414                         case 'select-one':
2415                         case 'select-multiple':
2416                             for (var j = 0; j < el.options.length; j++) {
2417                                 if (el.options[j].selected) {
2418                                     if (Roo.isIE) {
2419                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2420                                     }
2421                                     else {
2422                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2423                                     }
2424                                 }
2425                             }
2426                             break;
2427                         case 'radio':
2428                         case 'checkbox':
2429                             if (el.checked) {
2430                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2431                             }
2432                             break;
2433                         case 'file':
2434
2435                         case undefined:
2436
2437                         case 'reset':
2438
2439                         case 'button':
2440
2441                             break;
2442                         case 'submit':
2443                             if(hasSubmit == false) {
2444                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2445                                 hasSubmit = true;
2446                             }
2447                             break;
2448                         default:
2449                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2450                             break;
2451                     }
2452                 }
2453             }
2454             data = data.substr(0, data.length - 1);
2455             return data;
2456         },
2457
2458         headers:{},
2459
2460         hasHeaders:false,
2461
2462         useDefaultHeader:true,
2463
2464         defaultPostHeader:'application/x-www-form-urlencoded',
2465
2466         useDefaultXhrHeader:true,
2467
2468         defaultXhrHeader:'XMLHttpRequest',
2469
2470         hasDefaultHeaders:true,
2471
2472         defaultHeaders:{},
2473
2474         poll:{},
2475
2476         timeout:{},
2477
2478         pollInterval:50,
2479
2480         transactionId:0,
2481
2482         setProgId:function(id)
2483         {
2484             this.activeX.unshift(id);
2485         },
2486
2487         setDefaultPostHeader:function(b)
2488         {
2489             this.useDefaultHeader = b;
2490         },
2491
2492         setDefaultXhrHeader:function(b)
2493         {
2494             this.useDefaultXhrHeader = b;
2495         },
2496
2497         setPollingInterval:function(i)
2498         {
2499             if (typeof i == 'number' && isFinite(i)) {
2500                 this.pollInterval = i;
2501             }
2502         },
2503
2504         createXhrObject:function(transactionId)
2505         {
2506             var obj,http;
2507             try
2508             {
2509
2510                 http = new XMLHttpRequest();
2511
2512                 obj = { conn:http, tId:transactionId };
2513             }
2514             catch(e)
2515             {
2516                 for (var i = 0; i < this.activeX.length; ++i) {
2517                     try
2518                     {
2519
2520                         http = new ActiveXObject(this.activeX[i]);
2521
2522                         obj = { conn:http, tId:transactionId };
2523                         break;
2524                     }
2525                     catch(e) {
2526                     }
2527                 }
2528             }
2529             finally
2530             {
2531                 return obj;
2532             }
2533         },
2534
2535         getConnectionObject:function()
2536         {
2537             var o;
2538             var tId = this.transactionId;
2539
2540             try
2541             {
2542                 o = this.createXhrObject(tId);
2543                 if (o) {
2544                     this.transactionId++;
2545                 }
2546             }
2547             catch(e) {
2548             }
2549             finally
2550             {
2551                 return o;
2552             }
2553         },
2554
2555         asyncRequest:function(method, uri, callback, postData)
2556         {
2557             var o = this.getConnectionObject();
2558
2559             if (!o) {
2560                 return null;
2561             }
2562             else {
2563                 o.conn.open(method, uri, true);
2564
2565                 if (this.useDefaultXhrHeader) {
2566                     if (!this.defaultHeaders['X-Requested-With']) {
2567                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2568                     }
2569                 }
2570
2571                 if(postData && this.useDefaultHeader){
2572                     this.initHeader('Content-Type', this.defaultPostHeader);
2573                 }
2574
2575                  if (this.hasDefaultHeaders || this.hasHeaders) {
2576                     this.setHeader(o);
2577                 }
2578
2579                 this.handleReadyState(o, callback);
2580                 o.conn.send(postData || null);
2581
2582                 return o;
2583             }
2584         },
2585
2586         handleReadyState:function(o, callback)
2587         {
2588             var oConn = this;
2589
2590             if (callback && callback.timeout) {
2591                 this.timeout[o.tId] = window.setTimeout(function() {
2592                     oConn.abort(o, callback, true);
2593                 }, callback.timeout);
2594             }
2595
2596             this.poll[o.tId] = window.setInterval(
2597                     function() {
2598                         if (o.conn && o.conn.readyState == 4) {
2599                             window.clearInterval(oConn.poll[o.tId]);
2600                             delete oConn.poll[o.tId];
2601
2602                             if(callback && callback.timeout) {
2603                                 window.clearTimeout(oConn.timeout[o.tId]);
2604                                 delete oConn.timeout[o.tId];
2605                             }
2606
2607                             oConn.handleTransactionResponse(o, callback);
2608                         }
2609                     }
2610                     , this.pollInterval);
2611         },
2612
2613         handleTransactionResponse:function(o, callback, isAbort)
2614         {
2615
2616             if (!callback) {
2617                 this.releaseObject(o);
2618                 return;
2619             }
2620
2621             var httpStatus, responseObject;
2622
2623             try
2624             {
2625                 if (o.conn.status !== undefined && o.conn.status != 0) {
2626                     httpStatus = o.conn.status;
2627                 }
2628                 else {
2629                     httpStatus = 13030;
2630                 }
2631             }
2632             catch(e) {
2633
2634
2635                 httpStatus = 13030;
2636             }
2637
2638             if (httpStatus >= 200 && httpStatus < 300) {
2639                 responseObject = this.createResponseObject(o, callback.argument);
2640                 if (callback.success) {
2641                     if (!callback.scope) {
2642                         callback.success(responseObject);
2643                     }
2644                     else {
2645
2646
2647                         callback.success.apply(callback.scope, [responseObject]);
2648                     }
2649                 }
2650             }
2651             else {
2652                 switch (httpStatus) {
2653
2654                     case 12002:
2655                     case 12029:
2656                     case 12030:
2657                     case 12031:
2658                     case 12152:
2659                     case 13030:
2660                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2661                         if (callback.failure) {
2662                             if (!callback.scope) {
2663                                 callback.failure(responseObject);
2664                             }
2665                             else {
2666                                 callback.failure.apply(callback.scope, [responseObject]);
2667                             }
2668                         }
2669                         break;
2670                     default:
2671                         responseObject = this.createResponseObject(o, callback.argument);
2672                         if (callback.failure) {
2673                             if (!callback.scope) {
2674                                 callback.failure(responseObject);
2675                             }
2676                             else {
2677                                 callback.failure.apply(callback.scope, [responseObject]);
2678                             }
2679                         }
2680                 }
2681             }
2682
2683             this.releaseObject(o);
2684             responseObject = null;
2685         },
2686
2687         createResponseObject:function(o, callbackArg)
2688         {
2689             var obj = {};
2690             var headerObj = {};
2691
2692             try
2693             {
2694                 var headerStr = o.conn.getAllResponseHeaders();
2695                 var header = headerStr.split('\n');
2696                 for (var i = 0; i < header.length; i++) {
2697                     var delimitPos = header[i].indexOf(':');
2698                     if (delimitPos != -1) {
2699                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2700                     }
2701                 }
2702             }
2703             catch(e) {
2704             }
2705
2706             obj.tId = o.tId;
2707             obj.status = o.conn.status;
2708             obj.statusText = o.conn.statusText;
2709             obj.getResponseHeader = headerObj;
2710             obj.getAllResponseHeaders = headerStr;
2711             obj.responseText = o.conn.responseText;
2712             obj.responseXML = o.conn.responseXML;
2713
2714             if (typeof callbackArg !== undefined) {
2715                 obj.argument = callbackArg;
2716             }
2717
2718             return obj;
2719         },
2720
2721         createExceptionObject:function(tId, callbackArg, isAbort)
2722         {
2723             var COMM_CODE = 0;
2724             var COMM_ERROR = 'communication failure';
2725             var ABORT_CODE = -1;
2726             var ABORT_ERROR = 'transaction aborted';
2727
2728             var obj = {};
2729
2730             obj.tId = tId;
2731             if (isAbort) {
2732                 obj.status = ABORT_CODE;
2733                 obj.statusText = ABORT_ERROR;
2734             }
2735             else {
2736                 obj.status = COMM_CODE;
2737                 obj.statusText = COMM_ERROR;
2738             }
2739
2740             if (callbackArg) {
2741                 obj.argument = callbackArg;
2742             }
2743
2744             return obj;
2745         },
2746
2747         initHeader:function(label, value, isDefault)
2748         {
2749             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2750
2751             if (headerObj[label] === undefined) {
2752                 headerObj[label] = value;
2753             }
2754             else {
2755
2756
2757                 headerObj[label] = value + "," + headerObj[label];
2758             }
2759
2760             if (isDefault) {
2761                 this.hasDefaultHeaders = true;
2762             }
2763             else {
2764                 this.hasHeaders = true;
2765             }
2766         },
2767
2768
2769         setHeader:function(o)
2770         {
2771             if (this.hasDefaultHeaders) {
2772                 for (var prop in this.defaultHeaders) {
2773                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2774                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2775                     }
2776                 }
2777             }
2778
2779             if (this.hasHeaders) {
2780                 for (var prop in this.headers) {
2781                     if (this.headers.hasOwnProperty(prop)) {
2782                         o.conn.setRequestHeader(prop, this.headers[prop]);
2783                     }
2784                 }
2785                 this.headers = {};
2786                 this.hasHeaders = false;
2787             }
2788         },
2789
2790         resetDefaultHeaders:function() {
2791             delete this.defaultHeaders;
2792             this.defaultHeaders = {};
2793             this.hasDefaultHeaders = false;
2794         },
2795
2796         abort:function(o, callback, isTimeout)
2797         {
2798             if(this.isCallInProgress(o)) {
2799                 o.conn.abort();
2800                 window.clearInterval(this.poll[o.tId]);
2801                 delete this.poll[o.tId];
2802                 if (isTimeout) {
2803                     delete this.timeout[o.tId];
2804                 }
2805
2806                 this.handleTransactionResponse(o, callback, true);
2807
2808                 return true;
2809             }
2810             else {
2811                 return false;
2812             }
2813         },
2814
2815
2816         isCallInProgress:function(o)
2817         {
2818             if (o && o.conn) {
2819                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2820             }
2821             else {
2822
2823                 return false;
2824             }
2825         },
2826
2827
2828         releaseObject:function(o)
2829         {
2830
2831             o.conn = null;
2832
2833             o = null;
2834         },
2835
2836         activeX:[
2837         'MSXML2.XMLHTTP.3.0',
2838         'MSXML2.XMLHTTP',
2839         'Microsoft.XMLHTTP'
2840         ]
2841
2842
2843     };
2844 })();/*
2845  * Portions of this file are based on pieces of Yahoo User Interface Library
2846  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2847  * YUI licensed under the BSD License:
2848  * http://developer.yahoo.net/yui/license.txt
2849  * <script type="text/javascript">
2850  *
2851  */
2852
2853 Roo.lib.Region = function(t, r, b, l) {
2854     this.top = t;
2855     this[1] = t;
2856     this.right = r;
2857     this.bottom = b;
2858     this.left = l;
2859     this[0] = l;
2860 };
2861
2862
2863 Roo.lib.Region.prototype = {
2864     contains : function(region) {
2865         return ( region.left >= this.left &&
2866                  region.right <= this.right &&
2867                  region.top >= this.top &&
2868                  region.bottom <= this.bottom    );
2869
2870     },
2871
2872     getArea : function() {
2873         return ( (this.bottom - this.top) * (this.right - this.left) );
2874     },
2875
2876     intersect : function(region) {
2877         var t = Math.max(this.top, region.top);
2878         var r = Math.min(this.right, region.right);
2879         var b = Math.min(this.bottom, region.bottom);
2880         var l = Math.max(this.left, region.left);
2881
2882         if (b >= t && r >= l) {
2883             return new Roo.lib.Region(t, r, b, l);
2884         } else {
2885             return null;
2886         }
2887     },
2888     union : function(region) {
2889         var t = Math.min(this.top, region.top);
2890         var r = Math.max(this.right, region.right);
2891         var b = Math.max(this.bottom, region.bottom);
2892         var l = Math.min(this.left, region.left);
2893
2894         return new Roo.lib.Region(t, r, b, l);
2895     },
2896
2897     adjust : function(t, l, b, r) {
2898         this.top += t;
2899         this.left += l;
2900         this.right += r;
2901         this.bottom += b;
2902         return this;
2903     }
2904 };
2905
2906 Roo.lib.Region.getRegion = function(el) {
2907     var p = Roo.lib.Dom.getXY(el);
2908
2909     var t = p[1];
2910     var r = p[0] + el.offsetWidth;
2911     var b = p[1] + el.offsetHeight;
2912     var l = p[0];
2913
2914     return new Roo.lib.Region(t, r, b, l);
2915 };
2916 /*
2917  * Portions of this file are based on pieces of Yahoo User Interface Library
2918  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2919  * YUI licensed under the BSD License:
2920  * http://developer.yahoo.net/yui/license.txt
2921  * <script type="text/javascript">
2922  *
2923  */
2924 //@@dep Roo.lib.Region
2925
2926
2927 Roo.lib.Point = function(x, y) {
2928     if (x instanceof Array) {
2929         y = x[1];
2930         x = x[0];
2931     }
2932     this.x = this.right = this.left = this[0] = x;
2933     this.y = this.top = this.bottom = this[1] = y;
2934 };
2935
2936 Roo.lib.Point.prototype = new Roo.lib.Region();
2937 /*
2938  * Portions of this file are based on pieces of Yahoo User Interface Library
2939  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2940  * YUI licensed under the BSD License:
2941  * http://developer.yahoo.net/yui/license.txt
2942  * <script type="text/javascript">
2943  *
2944  */
2945  
2946 (function() {   
2947
2948     Roo.lib.Anim = {
2949         scroll : function(el, args, duration, easing, cb, scope) {
2950             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2951         },
2952
2953         motion : function(el, args, duration, easing, cb, scope) {
2954             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2955         },
2956
2957         color : function(el, args, duration, easing, cb, scope) {
2958             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2959         },
2960
2961         run : function(el, args, duration, easing, cb, scope, type) {
2962             type = type || Roo.lib.AnimBase;
2963             if (typeof easing == "string") {
2964                 easing = Roo.lib.Easing[easing];
2965             }
2966             var anim = new type(el, args, duration, easing);
2967             anim.animateX(function() {
2968                 Roo.callback(cb, scope);
2969             });
2970             return anim;
2971         }
2972     };
2973 })();/*
2974  * Portions of this file are based on pieces of Yahoo User Interface Library
2975  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2976  * YUI licensed under the BSD License:
2977  * http://developer.yahoo.net/yui/license.txt
2978  * <script type="text/javascript">
2979  *
2980  */
2981
2982 (function() {    
2983     var libFlyweight;
2984     
2985     function fly(el) {
2986         if (!libFlyweight) {
2987             libFlyweight = new Roo.Element.Flyweight();
2988         }
2989         libFlyweight.dom = el;
2990         return libFlyweight;
2991     }
2992
2993     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2994     
2995    
2996     
2997     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2998         if (el) {
2999             this.init(el, attributes, duration, method);
3000         }
3001     };
3002
3003     Roo.lib.AnimBase.fly = fly;
3004     
3005     
3006     
3007     Roo.lib.AnimBase.prototype = {
3008
3009         toString: function() {
3010             var el = this.getEl();
3011             var id = el.id || el.tagName;
3012             return ("Anim " + id);
3013         },
3014
3015         patterns: {
3016             noNegatives:        /width|height|opacity|padding/i,
3017             offsetAttribute:  /^((width|height)|(top|left))$/,
3018             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3019             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3020         },
3021
3022
3023         doMethod: function(attr, start, end) {
3024             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3025         },
3026
3027
3028         setAttribute: function(attr, val, unit) {
3029             if (this.patterns.noNegatives.test(attr)) {
3030                 val = (val > 0) ? val : 0;
3031             }
3032
3033             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3034         },
3035
3036
3037         getAttribute: function(attr) {
3038             var el = this.getEl();
3039             var val = fly(el).getStyle(attr);
3040
3041             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3042                 return parseFloat(val);
3043             }
3044
3045             var a = this.patterns.offsetAttribute.exec(attr) || [];
3046             var pos = !!( a[3] );
3047             var box = !!( a[2] );
3048
3049
3050             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3051                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3052             } else {
3053                 val = 0;
3054             }
3055
3056             return val;
3057         },
3058
3059
3060         getDefaultUnit: function(attr) {
3061             if (this.patterns.defaultUnit.test(attr)) {
3062                 return 'px';
3063             }
3064
3065             return '';
3066         },
3067
3068         animateX : function(callback, scope) {
3069             var f = function() {
3070                 this.onComplete.removeListener(f);
3071                 if (typeof callback == "function") {
3072                     callback.call(scope || this, this);
3073                 }
3074             };
3075             this.onComplete.addListener(f, this);
3076             this.animate();
3077         },
3078
3079
3080         setRuntimeAttribute: function(attr) {
3081             var start;
3082             var end;
3083             var attributes = this.attributes;
3084
3085             this.runtimeAttributes[attr] = {};
3086
3087             var isset = function(prop) {
3088                 return (typeof prop !== 'undefined');
3089             };
3090
3091             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3092                 return false;
3093             }
3094
3095             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3096
3097
3098             if (isset(attributes[attr]['to'])) {
3099                 end = attributes[attr]['to'];
3100             } else if (isset(attributes[attr]['by'])) {
3101                 if (start.constructor == Array) {
3102                     end = [];
3103                     for (var i = 0, len = start.length; i < len; ++i) {
3104                         end[i] = start[i] + attributes[attr]['by'][i];
3105                     }
3106                 } else {
3107                     end = start + attributes[attr]['by'];
3108                 }
3109             }
3110
3111             this.runtimeAttributes[attr].start = start;
3112             this.runtimeAttributes[attr].end = end;
3113
3114
3115             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3116         },
3117
3118
3119         init: function(el, attributes, duration, method) {
3120
3121             var isAnimated = false;
3122
3123
3124             var startTime = null;
3125
3126
3127             var actualFrames = 0;
3128
3129
3130             el = Roo.getDom(el);
3131
3132
3133             this.attributes = attributes || {};
3134
3135
3136             this.duration = duration || 1;
3137
3138
3139             this.method = method || Roo.lib.Easing.easeNone;
3140
3141
3142             this.useSeconds = true;
3143
3144
3145             this.currentFrame = 0;
3146
3147
3148             this.totalFrames = Roo.lib.AnimMgr.fps;
3149
3150
3151             this.getEl = function() {
3152                 return el;
3153             };
3154
3155
3156             this.isAnimated = function() {
3157                 return isAnimated;
3158             };
3159
3160
3161             this.getStartTime = function() {
3162                 return startTime;
3163             };
3164
3165             this.runtimeAttributes = {};
3166
3167
3168             this.animate = function() {
3169                 if (this.isAnimated()) {
3170                     return false;
3171                 }
3172
3173                 this.currentFrame = 0;
3174
3175                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3176
3177                 Roo.lib.AnimMgr.registerElement(this);
3178             };
3179
3180
3181             this.stop = function(finish) {
3182                 if (finish) {
3183                     this.currentFrame = this.totalFrames;
3184                     this._onTween.fire();
3185                 }
3186                 Roo.lib.AnimMgr.stop(this);
3187             };
3188
3189             var onStart = function() {
3190                 this.onStart.fire();
3191
3192                 this.runtimeAttributes = {};
3193                 for (var attr in this.attributes) {
3194                     this.setRuntimeAttribute(attr);
3195                 }
3196
3197                 isAnimated = true;
3198                 actualFrames = 0;
3199                 startTime = new Date();
3200             };
3201
3202
3203             var onTween = function() {
3204                 var data = {
3205                     duration: new Date() - this.getStartTime(),
3206                     currentFrame: this.currentFrame
3207                 };
3208
3209                 data.toString = function() {
3210                     return (
3211                             'duration: ' + data.duration +
3212                             ', currentFrame: ' + data.currentFrame
3213                             );
3214                 };
3215
3216                 this.onTween.fire(data);
3217
3218                 var runtimeAttributes = this.runtimeAttributes;
3219
3220                 for (var attr in runtimeAttributes) {
3221                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3222                 }
3223
3224                 actualFrames += 1;
3225             };
3226
3227             var onComplete = function() {
3228                 var actual_duration = (new Date() - startTime) / 1000 ;
3229
3230                 var data = {
3231                     duration: actual_duration,
3232                     frames: actualFrames,
3233                     fps: actualFrames / actual_duration
3234                 };
3235
3236                 data.toString = function() {
3237                     return (
3238                             'duration: ' + data.duration +
3239                             ', frames: ' + data.frames +
3240                             ', fps: ' + data.fps
3241                             );
3242                 };
3243
3244                 isAnimated = false;
3245                 actualFrames = 0;
3246                 this.onComplete.fire(data);
3247             };
3248
3249
3250             this._onStart = new Roo.util.Event(this);
3251             this.onStart = new Roo.util.Event(this);
3252             this.onTween = new Roo.util.Event(this);
3253             this._onTween = new Roo.util.Event(this);
3254             this.onComplete = new Roo.util.Event(this);
3255             this._onComplete = new Roo.util.Event(this);
3256             this._onStart.addListener(onStart);
3257             this._onTween.addListener(onTween);
3258             this._onComplete.addListener(onComplete);
3259         }
3260     };
3261 })();
3262 /*
3263  * Portions of this file are based on pieces of Yahoo User Interface Library
3264  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3265  * YUI licensed under the BSD License:
3266  * http://developer.yahoo.net/yui/license.txt
3267  * <script type="text/javascript">
3268  *
3269  */
3270
3271 Roo.lib.AnimMgr = new function() {
3272
3273         var thread = null;
3274
3275
3276         var queue = [];
3277
3278
3279         var tweenCount = 0;
3280
3281
3282         this.fps = 1000;
3283
3284
3285         this.delay = 1;
3286
3287
3288         this.registerElement = function(tween) {
3289             queue[queue.length] = tween;
3290             tweenCount += 1;
3291             tween._onStart.fire();
3292             this.start();
3293         };
3294
3295
3296         this.unRegister = function(tween, index) {
3297             tween._onComplete.fire();
3298             index = index || getIndex(tween);
3299             if (index != -1) {
3300                 queue.splice(index, 1);
3301             }
3302
3303             tweenCount -= 1;
3304             if (tweenCount <= 0) {
3305                 this.stop();
3306             }
3307         };
3308
3309
3310         this.start = function() {
3311             if (thread === null) {
3312                 thread = setInterval(this.run, this.delay);
3313             }
3314         };
3315
3316
3317         this.stop = function(tween) {
3318             if (!tween) {
3319                 clearInterval(thread);
3320
3321                 for (var i = 0, len = queue.length; i < len; ++i) {
3322                     if (queue[0].isAnimated()) {
3323                         this.unRegister(queue[0], 0);
3324                     }
3325                 }
3326
3327                 queue = [];
3328                 thread = null;
3329                 tweenCount = 0;
3330             }
3331             else {
3332                 this.unRegister(tween);
3333             }
3334         };
3335
3336
3337         this.run = function() {
3338             for (var i = 0, len = queue.length; i < len; ++i) {
3339                 var tween = queue[i];
3340                 if (!tween || !tween.isAnimated()) {
3341                     continue;
3342                 }
3343
3344                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3345                 {
3346                     tween.currentFrame += 1;
3347
3348                     if (tween.useSeconds) {
3349                         correctFrame(tween);
3350                     }
3351                     tween._onTween.fire();
3352                 }
3353                 else {
3354                     Roo.lib.AnimMgr.stop(tween, i);
3355                 }
3356             }
3357         };
3358
3359         var getIndex = function(anim) {
3360             for (var i = 0, len = queue.length; i < len; ++i) {
3361                 if (queue[i] == anim) {
3362                     return i;
3363                 }
3364             }
3365             return -1;
3366         };
3367
3368
3369         var correctFrame = function(tween) {
3370             var frames = tween.totalFrames;
3371             var frame = tween.currentFrame;
3372             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3373             var elapsed = (new Date() - tween.getStartTime());
3374             var tweak = 0;
3375
3376             if (elapsed < tween.duration * 1000) {
3377                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3378             } else {
3379                 tweak = frames - (frame + 1);
3380             }
3381             if (tweak > 0 && isFinite(tweak)) {
3382                 if (tween.currentFrame + tweak >= frames) {
3383                     tweak = frames - (frame + 1);
3384                 }
3385
3386                 tween.currentFrame += tweak;
3387             }
3388         };
3389     };/*
3390  * Portions of this file are based on pieces of Yahoo User Interface Library
3391  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3392  * YUI licensed under the BSD License:
3393  * http://developer.yahoo.net/yui/license.txt
3394  * <script type="text/javascript">
3395  *
3396  */
3397 Roo.lib.Bezier = new function() {
3398
3399         this.getPosition = function(points, t) {
3400             var n = points.length;
3401             var tmp = [];
3402
3403             for (var i = 0; i < n; ++i) {
3404                 tmp[i] = [points[i][0], points[i][1]];
3405             }
3406
3407             for (var j = 1; j < n; ++j) {
3408                 for (i = 0; i < n - j; ++i) {
3409                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3410                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3411                 }
3412             }
3413
3414             return [ tmp[0][0], tmp[0][1] ];
3415
3416         };
3417     };/*
3418  * Portions of this file are based on pieces of Yahoo User Interface Library
3419  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3420  * YUI licensed under the BSD License:
3421  * http://developer.yahoo.net/yui/license.txt
3422  * <script type="text/javascript">
3423  *
3424  */
3425 (function() {
3426
3427     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3428         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3429     };
3430
3431     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3432
3433     var fly = Roo.lib.AnimBase.fly;
3434     var Y = Roo.lib;
3435     var superclass = Y.ColorAnim.superclass;
3436     var proto = Y.ColorAnim.prototype;
3437
3438     proto.toString = function() {
3439         var el = this.getEl();
3440         var id = el.id || el.tagName;
3441         return ("ColorAnim " + id);
3442     };
3443
3444     proto.patterns.color = /color$/i;
3445     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3446     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3447     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3448     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3449
3450
3451     proto.parseColor = function(s) {
3452         if (s.length == 3) {
3453             return s;
3454         }
3455
3456         var c = this.patterns.hex.exec(s);
3457         if (c && c.length == 4) {
3458             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3459         }
3460
3461         c = this.patterns.rgb.exec(s);
3462         if (c && c.length == 4) {
3463             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3464         }
3465
3466         c = this.patterns.hex3.exec(s);
3467         if (c && c.length == 4) {
3468             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3469         }
3470
3471         return null;
3472     };
3473     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3474     proto.getAttribute = function(attr) {
3475         var el = this.getEl();
3476         if (this.patterns.color.test(attr)) {
3477             var val = fly(el).getStyle(attr);
3478
3479             if (this.patterns.transparent.test(val)) {
3480                 var parent = el.parentNode;
3481                 val = fly(parent).getStyle(attr);
3482
3483                 while (parent && this.patterns.transparent.test(val)) {
3484                     parent = parent.parentNode;
3485                     val = fly(parent).getStyle(attr);
3486                     if (parent.tagName.toUpperCase() == 'HTML') {
3487                         val = '#fff';
3488                     }
3489                 }
3490             }
3491         } else {
3492             val = superclass.getAttribute.call(this, attr);
3493         }
3494
3495         return val;
3496     };
3497     proto.getAttribute = function(attr) {
3498         var el = this.getEl();
3499         if (this.patterns.color.test(attr)) {
3500             var val = fly(el).getStyle(attr);
3501
3502             if (this.patterns.transparent.test(val)) {
3503                 var parent = el.parentNode;
3504                 val = fly(parent).getStyle(attr);
3505
3506                 while (parent && this.patterns.transparent.test(val)) {
3507                     parent = parent.parentNode;
3508                     val = fly(parent).getStyle(attr);
3509                     if (parent.tagName.toUpperCase() == 'HTML') {
3510                         val = '#fff';
3511                     }
3512                 }
3513             }
3514         } else {
3515             val = superclass.getAttribute.call(this, attr);
3516         }
3517
3518         return val;
3519     };
3520
3521     proto.doMethod = function(attr, start, end) {
3522         var val;
3523
3524         if (this.patterns.color.test(attr)) {
3525             val = [];
3526             for (var i = 0, len = start.length; i < len; ++i) {
3527                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3528             }
3529
3530             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3531         }
3532         else {
3533             val = superclass.doMethod.call(this, attr, start, end);
3534         }
3535
3536         return val;
3537     };
3538
3539     proto.setRuntimeAttribute = function(attr) {
3540         superclass.setRuntimeAttribute.call(this, attr);
3541
3542         if (this.patterns.color.test(attr)) {
3543             var attributes = this.attributes;
3544             var start = this.parseColor(this.runtimeAttributes[attr].start);
3545             var end = this.parseColor(this.runtimeAttributes[attr].end);
3546
3547             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3548                 end = this.parseColor(attributes[attr].by);
3549
3550                 for (var i = 0, len = start.length; i < len; ++i) {
3551                     end[i] = start[i] + end[i];
3552                 }
3553             }
3554
3555             this.runtimeAttributes[attr].start = start;
3556             this.runtimeAttributes[attr].end = end;
3557         }
3558     };
3559 })();
3560
3561 /*
3562  * Portions of this file are based on pieces of Yahoo User Interface Library
3563  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3564  * YUI licensed under the BSD License:
3565  * http://developer.yahoo.net/yui/license.txt
3566  * <script type="text/javascript">
3567  *
3568  */
3569 Roo.lib.Easing = {
3570
3571
3572     easeNone: function (t, b, c, d) {
3573         return c * t / d + b;
3574     },
3575
3576
3577     easeIn: function (t, b, c, d) {
3578         return c * (t /= d) * t + b;
3579     },
3580
3581
3582     easeOut: function (t, b, c, d) {
3583         return -c * (t /= d) * (t - 2) + b;
3584     },
3585
3586
3587     easeBoth: function (t, b, c, d) {
3588         if ((t /= d / 2) < 1) {
3589             return c / 2 * t * t + b;
3590         }
3591
3592         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3593     },
3594
3595
3596     easeInStrong: function (t, b, c, d) {
3597         return c * (t /= d) * t * t * t + b;
3598     },
3599
3600
3601     easeOutStrong: function (t, b, c, d) {
3602         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3603     },
3604
3605
3606     easeBothStrong: function (t, b, c, d) {
3607         if ((t /= d / 2) < 1) {
3608             return c / 2 * t * t * t * t + b;
3609         }
3610
3611         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3612     },
3613
3614
3615
3616     elasticIn: function (t, b, c, d, a, p) {
3617         if (t == 0) {
3618             return b;
3619         }
3620         if ((t /= d) == 1) {
3621             return b + c;
3622         }
3623         if (!p) {
3624             p = d * .3;
3625         }
3626
3627         if (!a || a < Math.abs(c)) {
3628             a = c;
3629             var s = p / 4;
3630         }
3631         else {
3632             var s = p / (2 * Math.PI) * Math.asin(c / a);
3633         }
3634
3635         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3636     },
3637
3638
3639     elasticOut: function (t, b, c, d, a, p) {
3640         if (t == 0) {
3641             return b;
3642         }
3643         if ((t /= d) == 1) {
3644             return b + c;
3645         }
3646         if (!p) {
3647             p = d * .3;
3648         }
3649
3650         if (!a || a < Math.abs(c)) {
3651             a = c;
3652             var s = p / 4;
3653         }
3654         else {
3655             var s = p / (2 * Math.PI) * Math.asin(c / a);
3656         }
3657
3658         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3659     },
3660
3661
3662     elasticBoth: function (t, b, c, d, a, p) {
3663         if (t == 0) {
3664             return b;
3665         }
3666
3667         if ((t /= d / 2) == 2) {
3668             return b + c;
3669         }
3670
3671         if (!p) {
3672             p = d * (.3 * 1.5);
3673         }
3674
3675         if (!a || a < Math.abs(c)) {
3676             a = c;
3677             var s = p / 4;
3678         }
3679         else {
3680             var s = p / (2 * Math.PI) * Math.asin(c / a);
3681         }
3682
3683         if (t < 1) {
3684             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3685                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3686         }
3687         return a * Math.pow(2, -10 * (t -= 1)) *
3688                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3689     },
3690
3691
3692
3693     backIn: function (t, b, c, d, s) {
3694         if (typeof s == 'undefined') {
3695             s = 1.70158;
3696         }
3697         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3698     },
3699
3700
3701     backOut: function (t, b, c, d, s) {
3702         if (typeof s == 'undefined') {
3703             s = 1.70158;
3704         }
3705         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3706     },
3707
3708
3709     backBoth: function (t, b, c, d, s) {
3710         if (typeof s == 'undefined') {
3711             s = 1.70158;
3712         }
3713
3714         if ((t /= d / 2 ) < 1) {
3715             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3716         }
3717         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3718     },
3719
3720
3721     bounceIn: function (t, b, c, d) {
3722         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3723     },
3724
3725
3726     bounceOut: function (t, b, c, d) {
3727         if ((t /= d) < (1 / 2.75)) {
3728             return c * (7.5625 * t * t) + b;
3729         } else if (t < (2 / 2.75)) {
3730             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3731         } else if (t < (2.5 / 2.75)) {
3732             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3733         }
3734         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3735     },
3736
3737
3738     bounceBoth: function (t, b, c, d) {
3739         if (t < d / 2) {
3740             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3741         }
3742         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3743     }
3744 };/*
3745  * Portions of this file are based on pieces of Yahoo User Interface Library
3746  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3747  * YUI licensed under the BSD License:
3748  * http://developer.yahoo.net/yui/license.txt
3749  * <script type="text/javascript">
3750  *
3751  */
3752     (function() {
3753         Roo.lib.Motion = function(el, attributes, duration, method) {
3754             if (el) {
3755                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3756             }
3757         };
3758
3759         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3760
3761
3762         var Y = Roo.lib;
3763         var superclass = Y.Motion.superclass;
3764         var proto = Y.Motion.prototype;
3765
3766         proto.toString = function() {
3767             var el = this.getEl();
3768             var id = el.id || el.tagName;
3769             return ("Motion " + id);
3770         };
3771
3772         proto.patterns.points = /^points$/i;
3773
3774         proto.setAttribute = function(attr, val, unit) {
3775             if (this.patterns.points.test(attr)) {
3776                 unit = unit || 'px';
3777                 superclass.setAttribute.call(this, 'left', val[0], unit);
3778                 superclass.setAttribute.call(this, 'top', val[1], unit);
3779             } else {
3780                 superclass.setAttribute.call(this, attr, val, unit);
3781             }
3782         };
3783
3784         proto.getAttribute = function(attr) {
3785             if (this.patterns.points.test(attr)) {
3786                 var val = [
3787                         superclass.getAttribute.call(this, 'left'),
3788                         superclass.getAttribute.call(this, 'top')
3789                         ];
3790             } else {
3791                 val = superclass.getAttribute.call(this, attr);
3792             }
3793
3794             return val;
3795         };
3796
3797         proto.doMethod = function(attr, start, end) {
3798             var val = null;
3799
3800             if (this.patterns.points.test(attr)) {
3801                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3802                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3803             } else {
3804                 val = superclass.doMethod.call(this, attr, start, end);
3805             }
3806             return val;
3807         };
3808
3809         proto.setRuntimeAttribute = function(attr) {
3810             if (this.patterns.points.test(attr)) {
3811                 var el = this.getEl();
3812                 var attributes = this.attributes;
3813                 var start;
3814                 var control = attributes['points']['control'] || [];
3815                 var end;
3816                 var i, len;
3817
3818                 if (control.length > 0 && !(control[0] instanceof Array)) {
3819                     control = [control];
3820                 } else {
3821                     var tmp = [];
3822                     for (i = 0,len = control.length; i < len; ++i) {
3823                         tmp[i] = control[i];
3824                     }
3825                     control = tmp;
3826                 }
3827
3828                 Roo.fly(el).position();
3829
3830                 if (isset(attributes['points']['from'])) {
3831                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3832                 }
3833                 else {
3834                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3835                 }
3836
3837                 start = this.getAttribute('points');
3838
3839
3840                 if (isset(attributes['points']['to'])) {
3841                     end = translateValues.call(this, attributes['points']['to'], start);
3842
3843                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3844                     for (i = 0,len = control.length; i < len; ++i) {
3845                         control[i] = translateValues.call(this, control[i], start);
3846                     }
3847
3848
3849                 } else if (isset(attributes['points']['by'])) {
3850                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3851
3852                     for (i = 0,len = control.length; i < len; ++i) {
3853                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3854                     }
3855                 }
3856
3857                 this.runtimeAttributes[attr] = [start];
3858
3859                 if (control.length > 0) {
3860                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3861                 }
3862
3863                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3864             }
3865             else {
3866                 superclass.setRuntimeAttribute.call(this, attr);
3867             }
3868         };
3869
3870         var translateValues = function(val, start) {
3871             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3872             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3873
3874             return val;
3875         };
3876
3877         var isset = function(prop) {
3878             return (typeof prop !== 'undefined');
3879         };
3880     })();
3881 /*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Scroll = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Scroll.superclass;
3901         var proto = Y.Scroll.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Scroll " + id);
3907         };
3908
3909         proto.doMethod = function(attr, start, end) {
3910             var val = null;
3911
3912             if (attr == 'scroll') {
3913                 val = [
3914                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3915                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3916                         ];
3917
3918             } else {
3919                 val = superclass.doMethod.call(this, attr, start, end);
3920             }
3921             return val;
3922         };
3923
3924         proto.getAttribute = function(attr) {
3925             var val = null;
3926             var el = this.getEl();
3927
3928             if (attr == 'scroll') {
3929                 val = [ el.scrollLeft, el.scrollTop ];
3930             } else {
3931                 val = superclass.getAttribute.call(this, attr);
3932             }
3933
3934             return val;
3935         };
3936
3937         proto.setAttribute = function(attr, val, unit) {
3938             var el = this.getEl();
3939
3940             if (attr == 'scroll') {
3941                 el.scrollLeft = val[0];
3942                 el.scrollTop = val[1];
3943             } else {
3944                 superclass.setAttribute.call(this, attr, val, unit);
3945             }
3946         };
3947     })();
3948 /*
3949  * Based on:
3950  * Ext JS Library 1.1.1
3951  * Copyright(c) 2006-2007, Ext JS, LLC.
3952  *
3953  * Originally Released Under LGPL - original licence link has changed is not relivant.
3954  *
3955  * Fork - LGPL
3956  * <script type="text/javascript">
3957  */
3958  
3959
3960 /**
3961  * @class Roo.DomHelper
3962  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3963  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3964  * @singleton
3965  */
3966 Roo.DomHelper = function(){
3967     var tempTableEl = null;
3968     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3969     var tableRe = /^table|tbody|tr|td$/i;
3970     var xmlns = {};
3971     // build as innerHTML where available
3972     /** @ignore */
3973     var createHtml = function(o){
3974         if(typeof o == 'string'){
3975             return o;
3976         }
3977         var b = "";
3978         if(!o.tag){
3979             o.tag = "div";
3980         }
3981         b += "<" + o.tag;
3982         for(var attr in o){
3983             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3984             if(attr == "style"){
3985                 var s = o["style"];
3986                 if(typeof s == "function"){
3987                     s = s.call();
3988                 }
3989                 if(typeof s == "string"){
3990                     b += ' style="' + s + '"';
3991                 }else if(typeof s == "object"){
3992                     b += ' style="';
3993                     for(var key in s){
3994                         if(typeof s[key] != "function"){
3995                             b += key + ":" + s[key] + ";";
3996                         }
3997                     }
3998                     b += '"';
3999                 }
4000             }else{
4001                 if(attr == "cls"){
4002                     b += ' class="' + o["cls"] + '"';
4003                 }else if(attr == "htmlFor"){
4004                     b += ' for="' + o["htmlFor"] + '"';
4005                 }else{
4006                     b += " " + attr + '="' + o[attr] + '"';
4007                 }
4008             }
4009         }
4010         if(emptyTags.test(o.tag)){
4011             b += "/>";
4012         }else{
4013             b += ">";
4014             var cn = o.children || o.cn;
4015             if(cn){
4016                 //http://bugs.kde.org/show_bug.cgi?id=71506
4017                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4018                     for(var i = 0, len = cn.length; i < len; i++) {
4019                         b += createHtml(cn[i], b);
4020                     }
4021                 }else{
4022                     b += createHtml(cn, b);
4023                 }
4024             }
4025             if(o.html){
4026                 b += o.html;
4027             }
4028             b += "</" + o.tag + ">";
4029         }
4030         return b;
4031     };
4032
4033     // build as dom
4034     /** @ignore */
4035     var createDom = function(o, parentNode){
4036          
4037         // defininition craeted..
4038         var ns = false;
4039         if (o.ns && o.ns != 'html') {
4040                
4041             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4042                 xmlns[o.ns] = o.xmlns;
4043                 ns = o.xmlns;
4044             }
4045             if (typeof(xmlns[o.ns]) == 'undefined') {
4046                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4047             }
4048             ns = xmlns[o.ns];
4049         }
4050         
4051         
4052         if (typeof(o) == 'string') {
4053             return parentNode.appendChild(document.createTextNode(o));
4054         }
4055         o.tag = o.tag || div;
4056         if (o.ns && Roo.isIE) {
4057             ns = false;
4058             o.tag = o.ns + ':' + o.tag;
4059             
4060         }
4061         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4062         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4063         for(var attr in o){
4064             
4065             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4066                     attr == "style" || typeof o[attr] == "function") continue;
4067                     
4068             if(attr=="cls" && Roo.isIE){
4069                 el.className = o["cls"];
4070             }else{
4071                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4072                 else el[attr] = o[attr];
4073             }
4074         }
4075         Roo.DomHelper.applyStyles(el, o.style);
4076         var cn = o.children || o.cn;
4077         if(cn){
4078             //http://bugs.kde.org/show_bug.cgi?id=71506
4079              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4080                 for(var i = 0, len = cn.length; i < len; i++) {
4081                     createDom(cn[i], el);
4082                 }
4083             }else{
4084                 createDom(cn, el);
4085             }
4086         }
4087         if(o.html){
4088             el.innerHTML = o.html;
4089         }
4090         if(parentNode){
4091            parentNode.appendChild(el);
4092         }
4093         return el;
4094     };
4095
4096     var ieTable = function(depth, s, h, e){
4097         tempTableEl.innerHTML = [s, h, e].join('');
4098         var i = -1, el = tempTableEl;
4099         while(++i < depth){
4100             el = el.firstChild;
4101         }
4102         return el;
4103     };
4104
4105     // kill repeat to save bytes
4106     var ts = '<table>',
4107         te = '</table>',
4108         tbs = ts+'<tbody>',
4109         tbe = '</tbody>'+te,
4110         trs = tbs + '<tr>',
4111         tre = '</tr>'+tbe;
4112
4113     /**
4114      * @ignore
4115      * Nasty code for IE's broken table implementation
4116      */
4117     var insertIntoTable = function(tag, where, el, html){
4118         if(!tempTableEl){
4119             tempTableEl = document.createElement('div');
4120         }
4121         var node;
4122         var before = null;
4123         if(tag == 'td'){
4124             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4125                 return;
4126             }
4127             if(where == 'beforebegin'){
4128                 before = el;
4129                 el = el.parentNode;
4130             } else{
4131                 before = el.nextSibling;
4132                 el = el.parentNode;
4133             }
4134             node = ieTable(4, trs, html, tre);
4135         }
4136         else if(tag == 'tr'){
4137             if(where == 'beforebegin'){
4138                 before = el;
4139                 el = el.parentNode;
4140                 node = ieTable(3, tbs, html, tbe);
4141             } else if(where == 'afterend'){
4142                 before = el.nextSibling;
4143                 el = el.parentNode;
4144                 node = ieTable(3, tbs, html, tbe);
4145             } else{ // INTO a TR
4146                 if(where == 'afterbegin'){
4147                     before = el.firstChild;
4148                 }
4149                 node = ieTable(4, trs, html, tre);
4150             }
4151         } else if(tag == 'tbody'){
4152             if(where == 'beforebegin'){
4153                 before = el;
4154                 el = el.parentNode;
4155                 node = ieTable(2, ts, html, te);
4156             } else if(where == 'afterend'){
4157                 before = el.nextSibling;
4158                 el = el.parentNode;
4159                 node = ieTable(2, ts, html, te);
4160             } else{
4161                 if(where == 'afterbegin'){
4162                     before = el.firstChild;
4163                 }
4164                 node = ieTable(3, tbs, html, tbe);
4165             }
4166         } else{ // TABLE
4167             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4168                 return;
4169             }
4170             if(where == 'afterbegin'){
4171                 before = el.firstChild;
4172             }
4173             node = ieTable(2, ts, html, te);
4174         }
4175         el.insertBefore(node, before);
4176         return node;
4177     };
4178
4179     return {
4180     /** True to force the use of DOM instead of html fragments @type Boolean */
4181     useDom : false,
4182
4183     /**
4184      * Returns the markup for the passed Element(s) config
4185      * @param {Object} o The Dom object spec (and children)
4186      * @return {String}
4187      */
4188     markup : function(o){
4189         return createHtml(o);
4190     },
4191
4192     /**
4193      * Applies a style specification to an element
4194      * @param {String/HTMLElement} el The element to apply styles to
4195      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4196      * a function which returns such a specification.
4197      */
4198     applyStyles : function(el, styles){
4199         if(styles){
4200            el = Roo.fly(el);
4201            if(typeof styles == "string"){
4202                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4203                var matches;
4204                while ((matches = re.exec(styles)) != null){
4205                    el.setStyle(matches[1], matches[2]);
4206                }
4207            }else if (typeof styles == "object"){
4208                for (var style in styles){
4209                   el.setStyle(style, styles[style]);
4210                }
4211            }else if (typeof styles == "function"){
4212                 Roo.DomHelper.applyStyles(el, styles.call());
4213            }
4214         }
4215     },
4216
4217     /**
4218      * Inserts an HTML fragment into the Dom
4219      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4220      * @param {HTMLElement} el The context element
4221      * @param {String} html The HTML fragmenet
4222      * @return {HTMLElement} The new node
4223      */
4224     insertHtml : function(where, el, html){
4225         where = where.toLowerCase();
4226         if(el.insertAdjacentHTML){
4227             if(tableRe.test(el.tagName)){
4228                 var rs;
4229                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4230                     return rs;
4231                 }
4232             }
4233             switch(where){
4234                 case "beforebegin":
4235                     el.insertAdjacentHTML('BeforeBegin', html);
4236                     return el.previousSibling;
4237                 case "afterbegin":
4238                     el.insertAdjacentHTML('AfterBegin', html);
4239                     return el.firstChild;
4240                 case "beforeend":
4241                     el.insertAdjacentHTML('BeforeEnd', html);
4242                     return el.lastChild;
4243                 case "afterend":
4244                     el.insertAdjacentHTML('AfterEnd', html);
4245                     return el.nextSibling;
4246             }
4247             throw 'Illegal insertion point -> "' + where + '"';
4248         }
4249         var range = el.ownerDocument.createRange();
4250         var frag;
4251         switch(where){
4252              case "beforebegin":
4253                 range.setStartBefore(el);
4254                 frag = range.createContextualFragment(html);
4255                 el.parentNode.insertBefore(frag, el);
4256                 return el.previousSibling;
4257              case "afterbegin":
4258                 if(el.firstChild){
4259                     range.setStartBefore(el.firstChild);
4260                     frag = range.createContextualFragment(html);
4261                     el.insertBefore(frag, el.firstChild);
4262                     return el.firstChild;
4263                 }else{
4264                     el.innerHTML = html;
4265                     return el.firstChild;
4266                 }
4267             case "beforeend":
4268                 if(el.lastChild){
4269                     range.setStartAfter(el.lastChild);
4270                     frag = range.createContextualFragment(html);
4271                     el.appendChild(frag);
4272                     return el.lastChild;
4273                 }else{
4274                     el.innerHTML = html;
4275                     return el.lastChild;
4276                 }
4277             case "afterend":
4278                 range.setStartAfter(el);
4279                 frag = range.createContextualFragment(html);
4280                 el.parentNode.insertBefore(frag, el.nextSibling);
4281                 return el.nextSibling;
4282             }
4283             throw 'Illegal insertion point -> "' + where + '"';
4284     },
4285
4286     /**
4287      * Creates new Dom element(s) and inserts them before el
4288      * @param {String/HTMLElement/Element} el The context element
4289      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4290      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4291      * @return {HTMLElement/Roo.Element} The new node
4292      */
4293     insertBefore : function(el, o, returnElement){
4294         return this.doInsert(el, o, returnElement, "beforeBegin");
4295     },
4296
4297     /**
4298      * Creates new Dom element(s) and inserts them after el
4299      * @param {String/HTMLElement/Element} el The context element
4300      * @param {Object} o The Dom object spec (and children)
4301      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4302      * @return {HTMLElement/Roo.Element} The new node
4303      */
4304     insertAfter : function(el, o, returnElement){
4305         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4306     },
4307
4308     /**
4309      * Creates new Dom element(s) and inserts them as the first child of el
4310      * @param {String/HTMLElement/Element} el The context element
4311      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4312      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4313      * @return {HTMLElement/Roo.Element} The new node
4314      */
4315     insertFirst : function(el, o, returnElement){
4316         return this.doInsert(el, o, returnElement, "afterBegin");
4317     },
4318
4319     // private
4320     doInsert : function(el, o, returnElement, pos, sibling){
4321         el = Roo.getDom(el);
4322         var newNode;
4323         if(this.useDom || o.ns){
4324             newNode = createDom(o, null);
4325             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4326         }else{
4327             var html = createHtml(o);
4328             newNode = this.insertHtml(pos, el, html);
4329         }
4330         return returnElement ? Roo.get(newNode, true) : newNode;
4331     },
4332
4333     /**
4334      * Creates new Dom element(s) and appends them to el
4335      * @param {String/HTMLElement/Element} el The context element
4336      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4337      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4338      * @return {HTMLElement/Roo.Element} The new node
4339      */
4340     append : function(el, o, returnElement){
4341         el = Roo.getDom(el);
4342         var newNode;
4343         if(this.useDom || o.ns){
4344             newNode = createDom(o, null);
4345             el.appendChild(newNode);
4346         }else{
4347             var html = createHtml(o);
4348             newNode = this.insertHtml("beforeEnd", el, html);
4349         }
4350         return returnElement ? Roo.get(newNode, true) : newNode;
4351     },
4352
4353     /**
4354      * Creates new Dom element(s) and overwrites the contents of el with them
4355      * @param {String/HTMLElement/Element} el The context element
4356      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4357      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4358      * @return {HTMLElement/Roo.Element} The new node
4359      */
4360     overwrite : function(el, o, returnElement){
4361         el = Roo.getDom(el);
4362         if (o.ns) {
4363           
4364             while (el.childNodes.length) {
4365                 el.removeChild(el.firstChild);
4366             }
4367             createDom(o, el);
4368         } else {
4369             el.innerHTML = createHtml(o);   
4370         }
4371         
4372         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4373     },
4374
4375     /**
4376      * Creates a new Roo.DomHelper.Template from the Dom object spec
4377      * @param {Object} o The Dom object spec (and children)
4378      * @return {Roo.DomHelper.Template} The new template
4379      */
4380     createTemplate : function(o){
4381         var html = createHtml(o);
4382         return new Roo.Template(html);
4383     }
4384     };
4385 }();
4386 /*
4387  * Based on:
4388  * Ext JS Library 1.1.1
4389  * Copyright(c) 2006-2007, Ext JS, LLC.
4390  *
4391  * Originally Released Under LGPL - original licence link has changed is not relivant.
4392  *
4393  * Fork - LGPL
4394  * <script type="text/javascript">
4395  */
4396  
4397 /**
4398 * @class Roo.Template
4399 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4400 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4401 * Usage:
4402 <pre><code>
4403 var t = new Roo.Template({
4404     html :  '&lt;div name="{id}"&gt;' + 
4405         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4406         '&lt;/div&gt;',
4407     myformat: function (value, allValues) {
4408         return 'XX' + value;
4409     }
4410 });
4411 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4412 </code></pre>
4413 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4414 * @constructor
4415 * @param {Object} cfg - Configuration object.
4416 */
4417 Roo.Template = function(cfg){
4418     // BC!
4419     if(cfg instanceof Array){
4420         cfg = cfg.join("");
4421     }else if(arguments.length > 1){
4422         cfg = Array.prototype.join.call(arguments, "");
4423     }
4424     
4425     
4426     if (typeof(cfg) == 'object') {
4427         Roo.apply(this,cfg)
4428     } else {
4429         // bc
4430         this.html = cfg;
4431     }
4432     
4433     
4434 };
4435 Roo.Template.prototype = {
4436     
4437     /**
4438      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4439      */
4440     html : '',
4441     /**
4442      * Returns an HTML fragment of this template with the specified values applied.
4443      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4444      * @return {String} The HTML fragment
4445      */
4446     applyTemplate : function(values){
4447         try {
4448             
4449             if(this.compiled){
4450                 return this.compiled(values);
4451             }
4452             var useF = this.disableFormats !== true;
4453             var fm = Roo.util.Format, tpl = this;
4454             var fn = function(m, name, format, args){
4455                 if(format && useF){
4456                     if(format.substr(0, 5) == "this."){
4457                         return tpl.call(format.substr(5), values[name], values);
4458                     }else{
4459                         if(args){
4460                             // quoted values are required for strings in compiled templates, 
4461                             // but for non compiled we need to strip them
4462                             // quoted reversed for jsmin
4463                             var re = /^\s*['"](.*)["']\s*$/;
4464                             args = args.split(',');
4465                             for(var i = 0, len = args.length; i < len; i++){
4466                                 args[i] = args[i].replace(re, "$1");
4467                             }
4468                             args = [values[name]].concat(args);
4469                         }else{
4470                             args = [values[name]];
4471                         }
4472                         return fm[format].apply(fm, args);
4473                     }
4474                 }else{
4475                     return values[name] !== undefined ? values[name] : "";
4476                 }
4477             };
4478             return this.html.replace(this.re, fn);
4479         } catch (e) {
4480             Roo.log(e);
4481             throw e;
4482         }
4483          
4484     },
4485     
4486     /**
4487      * Sets the HTML used as the template and optionally compiles it.
4488      * @param {String} html
4489      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4490      * @return {Roo.Template} this
4491      */
4492     set : function(html, compile){
4493         this.html = html;
4494         this.compiled = null;
4495         if(compile){
4496             this.compile();
4497         }
4498         return this;
4499     },
4500     
4501     /**
4502      * True to disable format functions (defaults to false)
4503      * @type Boolean
4504      */
4505     disableFormats : false,
4506     
4507     /**
4508     * The regular expression used to match template variables 
4509     * @type RegExp
4510     * @property 
4511     */
4512     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4513     
4514     /**
4515      * Compiles the template into an internal function, eliminating the RegEx overhead.
4516      * @return {Roo.Template} this
4517      */
4518     compile : function(){
4519         var fm = Roo.util.Format;
4520         var useF = this.disableFormats !== true;
4521         var sep = Roo.isGecko ? "+" : ",";
4522         var fn = function(m, name, format, args){
4523             if(format && useF){
4524                 args = args ? ',' + args : "";
4525                 if(format.substr(0, 5) != "this."){
4526                     format = "fm." + format + '(';
4527                 }else{
4528                     format = 'this.call("'+ format.substr(5) + '", ';
4529                     args = ", values";
4530                 }
4531             }else{
4532                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4533             }
4534             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4535         };
4536         var body;
4537         // branched to use + in gecko and [].join() in others
4538         if(Roo.isGecko){
4539             body = "this.compiled = function(values){ return '" +
4540                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4541                     "';};";
4542         }else{
4543             body = ["this.compiled = function(values){ return ['"];
4544             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4545             body.push("'].join('');};");
4546             body = body.join('');
4547         }
4548         /**
4549          * eval:var:values
4550          * eval:var:fm
4551          */
4552         eval(body);
4553         return this;
4554     },
4555     
4556     // private function used to call members
4557     call : function(fnName, value, allValues){
4558         return this[fnName](value, allValues);
4559     },
4560     
4561     /**
4562      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4563      * @param {String/HTMLElement/Roo.Element} el The context element
4564      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4565      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4566      * @return {HTMLElement/Roo.Element} The new node or Element
4567      */
4568     insertFirst: function(el, values, returnElement){
4569         return this.doInsert('afterBegin', el, values, returnElement);
4570     },
4571
4572     /**
4573      * Applies the supplied values to the template and inserts the new node(s) before el.
4574      * @param {String/HTMLElement/Roo.Element} el The context element
4575      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4576      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4577      * @return {HTMLElement/Roo.Element} The new node or Element
4578      */
4579     insertBefore: function(el, values, returnElement){
4580         return this.doInsert('beforeBegin', el, values, returnElement);
4581     },
4582
4583     /**
4584      * Applies the supplied values to the template and inserts the new node(s) after el.
4585      * @param {String/HTMLElement/Roo.Element} el The context element
4586      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4587      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4588      * @return {HTMLElement/Roo.Element} The new node or Element
4589      */
4590     insertAfter : function(el, values, returnElement){
4591         return this.doInsert('afterEnd', el, values, returnElement);
4592     },
4593     
4594     /**
4595      * Applies the supplied values to the template and appends the new node(s) to el.
4596      * @param {String/HTMLElement/Roo.Element} el The context element
4597      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4598      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4599      * @return {HTMLElement/Roo.Element} The new node or Element
4600      */
4601     append : function(el, values, returnElement){
4602         return this.doInsert('beforeEnd', el, values, returnElement);
4603     },
4604
4605     doInsert : function(where, el, values, returnEl){
4606         el = Roo.getDom(el);
4607         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4608         return returnEl ? Roo.get(newNode, true) : newNode;
4609     },
4610
4611     /**
4612      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4613      * @param {String/HTMLElement/Roo.Element} el The context element
4614      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4615      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4616      * @return {HTMLElement/Roo.Element} The new node or Element
4617      */
4618     overwrite : function(el, values, returnElement){
4619         el = Roo.getDom(el);
4620         el.innerHTML = this.applyTemplate(values);
4621         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4622     }
4623 };
4624 /**
4625  * Alias for {@link #applyTemplate}
4626  * @method
4627  */
4628 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4629
4630 // backwards compat
4631 Roo.DomHelper.Template = Roo.Template;
4632
4633 /**
4634  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4635  * @param {String/HTMLElement} el A DOM element or its id
4636  * @returns {Roo.Template} The created template
4637  * @static
4638  */
4639 Roo.Template.from = function(el){
4640     el = Roo.getDom(el);
4641     return new Roo.Template(el.value || el.innerHTML);
4642 };/*
4643  * Based on:
4644  * Ext JS Library 1.1.1
4645  * Copyright(c) 2006-2007, Ext JS, LLC.
4646  *
4647  * Originally Released Under LGPL - original licence link has changed is not relivant.
4648  *
4649  * Fork - LGPL
4650  * <script type="text/javascript">
4651  */
4652  
4653
4654 /*
4655  * This is code is also distributed under MIT license for use
4656  * with jQuery and prototype JavaScript libraries.
4657  */
4658 /**
4659  * @class Roo.DomQuery
4660 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4661 <p>
4662 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4663
4664 <p>
4665 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4666 </p>
4667 <h4>Element Selectors:</h4>
4668 <ul class="list">
4669     <li> <b>*</b> any element</li>
4670     <li> <b>E</b> an element with the tag E</li>
4671     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4672     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4673     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4674     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4675 </ul>
4676 <h4>Attribute Selectors:</h4>
4677 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4678 <ul class="list">
4679     <li> <b>E[foo]</b> has an attribute "foo"</li>
4680     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4681     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4682     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4683     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4684     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4685     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4686 </ul>
4687 <h4>Pseudo Classes:</h4>
4688 <ul class="list">
4689     <li> <b>E:first-child</b> E is the first child of its parent</li>
4690     <li> <b>E:last-child</b> E is the last child of its parent</li>
4691     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4692     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4693     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4694     <li> <b>E:only-child</b> E is the only child of its parent</li>
4695     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4696     <li> <b>E:first</b> the first E in the resultset</li>
4697     <li> <b>E:last</b> the last E in the resultset</li>
4698     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4699     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4700     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4701     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4702     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4703     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4704     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4705     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4706     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4707 </ul>
4708 <h4>CSS Value Selectors:</h4>
4709 <ul class="list">
4710     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4711     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4712     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4713     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4714     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4715     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4716 </ul>
4717  * @singleton
4718  */
4719 Roo.DomQuery = function(){
4720     var cache = {}, simpleCache = {}, valueCache = {};
4721     var nonSpace = /\S/;
4722     var trimRe = /^\s+|\s+$/g;
4723     var tplRe = /\{(\d+)\}/g;
4724     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4725     var tagTokenRe = /^(#)?([\w-\*]+)/;
4726     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4727
4728     function child(p, index){
4729         var i = 0;
4730         var n = p.firstChild;
4731         while(n){
4732             if(n.nodeType == 1){
4733                if(++i == index){
4734                    return n;
4735                }
4736             }
4737             n = n.nextSibling;
4738         }
4739         return null;
4740     };
4741
4742     function next(n){
4743         while((n = n.nextSibling) && n.nodeType != 1);
4744         return n;
4745     };
4746
4747     function prev(n){
4748         while((n = n.previousSibling) && n.nodeType != 1);
4749         return n;
4750     };
4751
4752     function children(d){
4753         var n = d.firstChild, ni = -1;
4754             while(n){
4755                 var nx = n.nextSibling;
4756                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4757                     d.removeChild(n);
4758                 }else{
4759                     n.nodeIndex = ++ni;
4760                 }
4761                 n = nx;
4762             }
4763             return this;
4764         };
4765
4766     function byClassName(c, a, v){
4767         if(!v){
4768             return c;
4769         }
4770         var r = [], ri = -1, cn;
4771         for(var i = 0, ci; ci = c[i]; i++){
4772             if((' '+ci.className+' ').indexOf(v) != -1){
4773                 r[++ri] = ci;
4774             }
4775         }
4776         return r;
4777     };
4778
4779     function attrValue(n, attr){
4780         if(!n.tagName && typeof n.length != "undefined"){
4781             n = n[0];
4782         }
4783         if(!n){
4784             return null;
4785         }
4786         if(attr == "for"){
4787             return n.htmlFor;
4788         }
4789         if(attr == "class" || attr == "className"){
4790             return n.className;
4791         }
4792         return n.getAttribute(attr) || n[attr];
4793
4794     };
4795
4796     function getNodes(ns, mode, tagName){
4797         var result = [], ri = -1, cs;
4798         if(!ns){
4799             return result;
4800         }
4801         tagName = tagName || "*";
4802         if(typeof ns.getElementsByTagName != "undefined"){
4803             ns = [ns];
4804         }
4805         if(!mode){
4806             for(var i = 0, ni; ni = ns[i]; i++){
4807                 cs = ni.getElementsByTagName(tagName);
4808                 for(var j = 0, ci; ci = cs[j]; j++){
4809                     result[++ri] = ci;
4810                 }
4811             }
4812         }else if(mode == "/" || mode == ">"){
4813             var utag = tagName.toUpperCase();
4814             for(var i = 0, ni, cn; ni = ns[i]; i++){
4815                 cn = ni.children || ni.childNodes;
4816                 for(var j = 0, cj; cj = cn[j]; j++){
4817                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4818                         result[++ri] = cj;
4819                     }
4820                 }
4821             }
4822         }else if(mode == "+"){
4823             var utag = tagName.toUpperCase();
4824             for(var i = 0, n; n = ns[i]; i++){
4825                 while((n = n.nextSibling) && n.nodeType != 1);
4826                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4827                     result[++ri] = n;
4828                 }
4829             }
4830         }else if(mode == "~"){
4831             for(var i = 0, n; n = ns[i]; i++){
4832                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4833                 if(n){
4834                     result[++ri] = n;
4835                 }
4836             }
4837         }
4838         return result;
4839     };
4840
4841     function concat(a, b){
4842         if(b.slice){
4843             return a.concat(b);
4844         }
4845         for(var i = 0, l = b.length; i < l; i++){
4846             a[a.length] = b[i];
4847         }
4848         return a;
4849     }
4850
4851     function byTag(cs, tagName){
4852         if(cs.tagName || cs == document){
4853             cs = [cs];
4854         }
4855         if(!tagName){
4856             return cs;
4857         }
4858         var r = [], ri = -1;
4859         tagName = tagName.toLowerCase();
4860         for(var i = 0, ci; ci = cs[i]; i++){
4861             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4862                 r[++ri] = ci;
4863             }
4864         }
4865         return r;
4866     };
4867
4868     function byId(cs, attr, id){
4869         if(cs.tagName || cs == document){
4870             cs = [cs];
4871         }
4872         if(!id){
4873             return cs;
4874         }
4875         var r = [], ri = -1;
4876         for(var i = 0,ci; ci = cs[i]; i++){
4877             if(ci && ci.id == id){
4878                 r[++ri] = ci;
4879                 return r;
4880             }
4881         }
4882         return r;
4883     };
4884
4885     function byAttribute(cs, attr, value, op, custom){
4886         var r = [], ri = -1, st = custom=="{";
4887         var f = Roo.DomQuery.operators[op];
4888         for(var i = 0, ci; ci = cs[i]; i++){
4889             var a;
4890             if(st){
4891                 a = Roo.DomQuery.getStyle(ci, attr);
4892             }
4893             else if(attr == "class" || attr == "className"){
4894                 a = ci.className;
4895             }else if(attr == "for"){
4896                 a = ci.htmlFor;
4897             }else if(attr == "href"){
4898                 a = ci.getAttribute("href", 2);
4899             }else{
4900                 a = ci.getAttribute(attr);
4901             }
4902             if((f && f(a, value)) || (!f && a)){
4903                 r[++ri] = ci;
4904             }
4905         }
4906         return r;
4907     };
4908
4909     function byPseudo(cs, name, value){
4910         return Roo.DomQuery.pseudos[name](cs, value);
4911     };
4912
4913     // This is for IE MSXML which does not support expandos.
4914     // IE runs the same speed using setAttribute, however FF slows way down
4915     // and Safari completely fails so they need to continue to use expandos.
4916     var isIE = window.ActiveXObject ? true : false;
4917
4918     // this eval is stop the compressor from
4919     // renaming the variable to something shorter
4920     
4921     /** eval:var:batch */
4922     var batch = 30803; 
4923
4924     var key = 30803;
4925
4926     function nodupIEXml(cs){
4927         var d = ++key;
4928         cs[0].setAttribute("_nodup", d);
4929         var r = [cs[0]];
4930         for(var i = 1, len = cs.length; i < len; i++){
4931             var c = cs[i];
4932             if(!c.getAttribute("_nodup") != d){
4933                 c.setAttribute("_nodup", d);
4934                 r[r.length] = c;
4935             }
4936         }
4937         for(var i = 0, len = cs.length; i < len; i++){
4938             cs[i].removeAttribute("_nodup");
4939         }
4940         return r;
4941     }
4942
4943     function nodup(cs){
4944         if(!cs){
4945             return [];
4946         }
4947         var len = cs.length, c, i, r = cs, cj, ri = -1;
4948         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4949             return cs;
4950         }
4951         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4952             return nodupIEXml(cs);
4953         }
4954         var d = ++key;
4955         cs[0]._nodup = d;
4956         for(i = 1; c = cs[i]; i++){
4957             if(c._nodup != d){
4958                 c._nodup = d;
4959             }else{
4960                 r = [];
4961                 for(var j = 0; j < i; j++){
4962                     r[++ri] = cs[j];
4963                 }
4964                 for(j = i+1; cj = cs[j]; j++){
4965                     if(cj._nodup != d){
4966                         cj._nodup = d;
4967                         r[++ri] = cj;
4968                     }
4969                 }
4970                 return r;
4971             }
4972         }
4973         return r;
4974     }
4975
4976     function quickDiffIEXml(c1, c2){
4977         var d = ++key;
4978         for(var i = 0, len = c1.length; i < len; i++){
4979             c1[i].setAttribute("_qdiff", d);
4980         }
4981         var r = [];
4982         for(var i = 0, len = c2.length; i < len; i++){
4983             if(c2[i].getAttribute("_qdiff") != d){
4984                 r[r.length] = c2[i];
4985             }
4986         }
4987         for(var i = 0, len = c1.length; i < len; i++){
4988            c1[i].removeAttribute("_qdiff");
4989         }
4990         return r;
4991     }
4992
4993     function quickDiff(c1, c2){
4994         var len1 = c1.length;
4995         if(!len1){
4996             return c2;
4997         }
4998         if(isIE && c1[0].selectSingleNode){
4999             return quickDiffIEXml(c1, c2);
5000         }
5001         var d = ++key;
5002         for(var i = 0; i < len1; i++){
5003             c1[i]._qdiff = d;
5004         }
5005         var r = [];
5006         for(var i = 0, len = c2.length; i < len; i++){
5007             if(c2[i]._qdiff != d){
5008                 r[r.length] = c2[i];
5009             }
5010         }
5011         return r;
5012     }
5013
5014     function quickId(ns, mode, root, id){
5015         if(ns == root){
5016            var d = root.ownerDocument || root;
5017            return d.getElementById(id);
5018         }
5019         ns = getNodes(ns, mode, "*");
5020         return byId(ns, null, id);
5021     }
5022
5023     return {
5024         getStyle : function(el, name){
5025             return Roo.fly(el).getStyle(name);
5026         },
5027         /**
5028          * Compiles a selector/xpath query into a reusable function. The returned function
5029          * takes one parameter "root" (optional), which is the context node from where the query should start.
5030          * @param {String} selector The selector/xpath query
5031          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5032          * @return {Function}
5033          */
5034         compile : function(path, type){
5035             type = type || "select";
5036             
5037             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5038             var q = path, mode, lq;
5039             var tk = Roo.DomQuery.matchers;
5040             var tklen = tk.length;
5041             var mm;
5042
5043             // accept leading mode switch
5044             var lmode = q.match(modeRe);
5045             if(lmode && lmode[1]){
5046                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5047                 q = q.replace(lmode[1], "");
5048             }
5049             // strip leading slashes
5050             while(path.substr(0, 1)=="/"){
5051                 path = path.substr(1);
5052             }
5053
5054             while(q && lq != q){
5055                 lq = q;
5056                 var tm = q.match(tagTokenRe);
5057                 if(type == "select"){
5058                     if(tm){
5059                         if(tm[1] == "#"){
5060                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5061                         }else{
5062                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5063                         }
5064                         q = q.replace(tm[0], "");
5065                     }else if(q.substr(0, 1) != '@'){
5066                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5067                     }
5068                 }else{
5069                     if(tm){
5070                         if(tm[1] == "#"){
5071                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5072                         }else{
5073                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5074                         }
5075                         q = q.replace(tm[0], "");
5076                     }
5077                 }
5078                 while(!(mm = q.match(modeRe))){
5079                     var matched = false;
5080                     for(var j = 0; j < tklen; j++){
5081                         var t = tk[j];
5082                         var m = q.match(t.re);
5083                         if(m){
5084                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5085                                                     return m[i];
5086                                                 });
5087                             q = q.replace(m[0], "");
5088                             matched = true;
5089                             break;
5090                         }
5091                     }
5092                     // prevent infinite loop on bad selector
5093                     if(!matched){
5094                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5095                     }
5096                 }
5097                 if(mm[1]){
5098                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5099                     q = q.replace(mm[1], "");
5100                 }
5101             }
5102             fn[fn.length] = "return nodup(n);\n}";
5103             
5104              /** 
5105               * list of variables that need from compression as they are used by eval.
5106              *  eval:var:batch 
5107              *  eval:var:nodup
5108              *  eval:var:byTag
5109              *  eval:var:ById
5110              *  eval:var:getNodes
5111              *  eval:var:quickId
5112              *  eval:var:mode
5113              *  eval:var:root
5114              *  eval:var:n
5115              *  eval:var:byClassName
5116              *  eval:var:byPseudo
5117              *  eval:var:byAttribute
5118              *  eval:var:attrValue
5119              * 
5120              **/ 
5121             eval(fn.join(""));
5122             return f;
5123         },
5124
5125         /**
5126          * Selects a group of elements.
5127          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5128          * @param {Node} root (optional) The start of the query (defaults to document).
5129          * @return {Array}
5130          */
5131         select : function(path, root, type){
5132             if(!root || root == document){
5133                 root = document;
5134             }
5135             if(typeof root == "string"){
5136                 root = document.getElementById(root);
5137             }
5138             var paths = path.split(",");
5139             var results = [];
5140             for(var i = 0, len = paths.length; i < len; i++){
5141                 var p = paths[i].replace(trimRe, "");
5142                 if(!cache[p]){
5143                     cache[p] = Roo.DomQuery.compile(p);
5144                     if(!cache[p]){
5145                         throw p + " is not a valid selector";
5146                     }
5147                 }
5148                 var result = cache[p](root);
5149                 if(result && result != document){
5150                     results = results.concat(result);
5151                 }
5152             }
5153             if(paths.length > 1){
5154                 return nodup(results);
5155             }
5156             return results;
5157         },
5158
5159         /**
5160          * Selects a single element.
5161          * @param {String} selector The selector/xpath query
5162          * @param {Node} root (optional) The start of the query (defaults to document).
5163          * @return {Element}
5164          */
5165         selectNode : function(path, root){
5166             return Roo.DomQuery.select(path, root)[0];
5167         },
5168
5169         /**
5170          * Selects the value of a node, optionally replacing null with the defaultValue.
5171          * @param {String} selector The selector/xpath query
5172          * @param {Node} root (optional) The start of the query (defaults to document).
5173          * @param {String} defaultValue
5174          */
5175         selectValue : function(path, root, defaultValue){
5176             path = path.replace(trimRe, "");
5177             if(!valueCache[path]){
5178                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5179             }
5180             var n = valueCache[path](root);
5181             n = n[0] ? n[0] : n;
5182             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5183             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5184         },
5185
5186         /**
5187          * Selects the value of a node, parsing integers and floats.
5188          * @param {String} selector The selector/xpath query
5189          * @param {Node} root (optional) The start of the query (defaults to document).
5190          * @param {Number} defaultValue
5191          * @return {Number}
5192          */
5193         selectNumber : function(path, root, defaultValue){
5194             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5195             return parseFloat(v);
5196         },
5197
5198         /**
5199          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5200          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5201          * @param {String} selector The simple selector to test
5202          * @return {Boolean}
5203          */
5204         is : function(el, ss){
5205             if(typeof el == "string"){
5206                 el = document.getElementById(el);
5207             }
5208             var isArray = (el instanceof Array);
5209             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5210             return isArray ? (result.length == el.length) : (result.length > 0);
5211         },
5212
5213         /**
5214          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5215          * @param {Array} el An array of elements to filter
5216          * @param {String} selector The simple selector to test
5217          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5218          * the selector instead of the ones that match
5219          * @return {Array}
5220          */
5221         filter : function(els, ss, nonMatches){
5222             ss = ss.replace(trimRe, "");
5223             if(!simpleCache[ss]){
5224                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5225             }
5226             var result = simpleCache[ss](els);
5227             return nonMatches ? quickDiff(result, els) : result;
5228         },
5229
5230         /**
5231          * Collection of matching regular expressions and code snippets.
5232          */
5233         matchers : [{
5234                 re: /^\.([\w-]+)/,
5235                 select: 'n = byClassName(n, null, " {1} ");'
5236             }, {
5237                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5238                 select: 'n = byPseudo(n, "{1}", "{2}");'
5239             },{
5240                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5241                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5242             }, {
5243                 re: /^#([\w-]+)/,
5244                 select: 'n = byId(n, null, "{1}");'
5245             },{
5246                 re: /^@([\w-]+)/,
5247                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5248             }
5249         ],
5250
5251         /**
5252          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5253          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5254          */
5255         operators : {
5256             "=" : function(a, v){
5257                 return a == v;
5258             },
5259             "!=" : function(a, v){
5260                 return a != v;
5261             },
5262             "^=" : function(a, v){
5263                 return a && a.substr(0, v.length) == v;
5264             },
5265             "$=" : function(a, v){
5266                 return a && a.substr(a.length-v.length) == v;
5267             },
5268             "*=" : function(a, v){
5269                 return a && a.indexOf(v) !== -1;
5270             },
5271             "%=" : function(a, v){
5272                 return (a % v) == 0;
5273             },
5274             "|=" : function(a, v){
5275                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5276             },
5277             "~=" : function(a, v){
5278                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5279             }
5280         },
5281
5282         /**
5283          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5284          * and the argument (if any) supplied in the selector.
5285          */
5286         pseudos : {
5287             "first-child" : function(c){
5288                 var r = [], ri = -1, n;
5289                 for(var i = 0, ci; ci = n = c[i]; i++){
5290                     while((n = n.previousSibling) && n.nodeType != 1);
5291                     if(!n){
5292                         r[++ri] = ci;
5293                     }
5294                 }
5295                 return r;
5296             },
5297
5298             "last-child" : function(c){
5299                 var r = [], ri = -1, n;
5300                 for(var i = 0, ci; ci = n = c[i]; i++){
5301                     while((n = n.nextSibling) && n.nodeType != 1);
5302                     if(!n){
5303                         r[++ri] = ci;
5304                     }
5305                 }
5306                 return r;
5307             },
5308
5309             "nth-child" : function(c, a) {
5310                 var r = [], ri = -1;
5311                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5312                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5313                 for(var i = 0, n; n = c[i]; i++){
5314                     var pn = n.parentNode;
5315                     if (batch != pn._batch) {
5316                         var j = 0;
5317                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5318                             if(cn.nodeType == 1){
5319                                cn.nodeIndex = ++j;
5320                             }
5321                         }
5322                         pn._batch = batch;
5323                     }
5324                     if (f == 1) {
5325                         if (l == 0 || n.nodeIndex == l){
5326                             r[++ri] = n;
5327                         }
5328                     } else if ((n.nodeIndex + l) % f == 0){
5329                         r[++ri] = n;
5330                     }
5331                 }
5332
5333                 return r;
5334             },
5335
5336             "only-child" : function(c){
5337                 var r = [], ri = -1;;
5338                 for(var i = 0, ci; ci = c[i]; i++){
5339                     if(!prev(ci) && !next(ci)){
5340                         r[++ri] = ci;
5341                     }
5342                 }
5343                 return r;
5344             },
5345
5346             "empty" : function(c){
5347                 var r = [], ri = -1;
5348                 for(var i = 0, ci; ci = c[i]; i++){
5349                     var cns = ci.childNodes, j = 0, cn, empty = true;
5350                     while(cn = cns[j]){
5351                         ++j;
5352                         if(cn.nodeType == 1 || cn.nodeType == 3){
5353                             empty = false;
5354                             break;
5355                         }
5356                     }
5357                     if(empty){
5358                         r[++ri] = ci;
5359                     }
5360                 }
5361                 return r;
5362             },
5363
5364             "contains" : function(c, v){
5365                 var r = [], ri = -1;
5366                 for(var i = 0, ci; ci = c[i]; i++){
5367                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "nodeValue" : function(c, v){
5375                 var r = [], ri = -1;
5376                 for(var i = 0, ci; ci = c[i]; i++){
5377                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5378                         r[++ri] = ci;
5379                     }
5380                 }
5381                 return r;
5382             },
5383
5384             "checked" : function(c){
5385                 var r = [], ri = -1;
5386                 for(var i = 0, ci; ci = c[i]; i++){
5387                     if(ci.checked == true){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "not" : function(c, ss){
5395                 return Roo.DomQuery.filter(c, ss, true);
5396             },
5397
5398             "odd" : function(c){
5399                 return this["nth-child"](c, "odd");
5400             },
5401
5402             "even" : function(c){
5403                 return this["nth-child"](c, "even");
5404             },
5405
5406             "nth" : function(c, a){
5407                 return c[a-1] || [];
5408             },
5409
5410             "first" : function(c){
5411                 return c[0] || [];
5412             },
5413
5414             "last" : function(c){
5415                 return c[c.length-1] || [];
5416             },
5417
5418             "has" : function(c, ss){
5419                 var s = Roo.DomQuery.select;
5420                 var r = [], ri = -1;
5421                 for(var i = 0, ci; ci = c[i]; i++){
5422                     if(s(ss, ci).length > 0){
5423                         r[++ri] = ci;
5424                     }
5425                 }
5426                 return r;
5427             },
5428
5429             "next" : function(c, ss){
5430                 var is = Roo.DomQuery.is;
5431                 var r = [], ri = -1;
5432                 for(var i = 0, ci; ci = c[i]; i++){
5433                     var n = next(ci);
5434                     if(n && is(n, ss)){
5435                         r[++ri] = ci;
5436                     }
5437                 }
5438                 return r;
5439             },
5440
5441             "prev" : function(c, ss){
5442                 var is = Roo.DomQuery.is;
5443                 var r = [], ri = -1;
5444                 for(var i = 0, ci; ci = c[i]; i++){
5445                     var n = prev(ci);
5446                     if(n && is(n, ss)){
5447                         r[++ri] = ci;
5448                     }
5449                 }
5450                 return r;
5451             }
5452         }
5453     };
5454 }();
5455
5456 /**
5457  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5458  * @param {String} path The selector/xpath query
5459  * @param {Node} root (optional) The start of the query (defaults to document).
5460  * @return {Array}
5461  * @member Roo
5462  * @method query
5463  */
5464 Roo.query = Roo.DomQuery.select;
5465 /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475
5476 /**
5477  * @class Roo.util.Observable
5478  * Base class that provides a common interface for publishing events. Subclasses are expected to
5479  * to have a property "events" with all the events defined.<br>
5480  * For example:
5481  * <pre><code>
5482  Employee = function(name){
5483     this.name = name;
5484     this.addEvents({
5485         "fired" : true,
5486         "quit" : true
5487     });
5488  }
5489  Roo.extend(Employee, Roo.util.Observable);
5490 </code></pre>
5491  * @param {Object} config properties to use (incuding events / listeners)
5492  */
5493
5494 Roo.util.Observable = function(cfg){
5495     
5496     cfg = cfg|| {};
5497     this.addEvents(cfg.events || {});
5498     if (cfg.events) {
5499         delete cfg.events; // make sure
5500     }
5501      
5502     Roo.apply(this, cfg);
5503     
5504     if(this.listeners){
5505         this.on(this.listeners);
5506         delete this.listeners;
5507     }
5508 };
5509 Roo.util.Observable.prototype = {
5510     /** 
5511  * @cfg {Object} listeners  list of events and functions to call for this object, 
5512  * For example :
5513  * <pre><code>
5514     listeners :  { 
5515        'click' : function(e) {
5516            ..... 
5517         } ,
5518         .... 
5519     } 
5520   </code></pre>
5521  */
5522     
5523     
5524     /**
5525      * Fires the specified event with the passed parameters (minus the event name).
5526      * @param {String} eventName
5527      * @param {Object...} args Variable number of parameters are passed to handlers
5528      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5529      */
5530     fireEvent : function(){
5531         var ce = this.events[arguments[0].toLowerCase()];
5532         if(typeof ce == "object"){
5533             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5534         }else{
5535             return true;
5536         }
5537     },
5538
5539     // private
5540     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5541
5542     /**
5543      * Appends an event handler to this component
5544      * @param {String}   eventName The type of event to listen for
5545      * @param {Function} handler The method the event invokes
5546      * @param {Object}   scope (optional) The scope in which to execute the handler
5547      * function. The handler function's "this" context.
5548      * @param {Object}   options (optional) An object containing handler configuration
5549      * properties. This may contain any of the following properties:<ul>
5550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5551      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5552      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5553      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5554      * by the specified number of milliseconds. If the event fires again within that time, the original
5555      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5556      * </ul><br>
5557      * <p>
5558      * <b>Combining Options</b><br>
5559      * Using the options argument, it is possible to combine different types of listeners:<br>
5560      * <br>
5561      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5562                 <pre><code>
5563                 el.on('click', this.onClick, this, {
5564                         single: true,
5565                 delay: 100,
5566                 forumId: 4
5567                 });
5568                 </code></pre>
5569      * <p>
5570      * <b>Attaching multiple handlers in 1 call</b><br>
5571      * The method also allows for a single argument to be passed which is a config object containing properties
5572      * which specify multiple handlers.
5573      * <pre><code>
5574                 el.on({
5575                         'click': {
5576                         fn: this.onClick,
5577                         scope: this,
5578                         delay: 100
5579                 }, 
5580                 'mouseover': {
5581                         fn: this.onMouseOver,
5582                         scope: this
5583                 },
5584                 'mouseout': {
5585                         fn: this.onMouseOut,
5586                         scope: this
5587                 }
5588                 });
5589                 </code></pre>
5590      * <p>
5591      * Or a shorthand syntax which passes the same scope object to all handlers:
5592         <pre><code>
5593                 el.on({
5594                         'click': this.onClick,
5595                 'mouseover': this.onMouseOver,
5596                 'mouseout': this.onMouseOut,
5597                 scope: this
5598                 });
5599                 </code></pre>
5600      */
5601     addListener : function(eventName, fn, scope, o){
5602         if(typeof eventName == "object"){
5603             o = eventName;
5604             for(var e in o){
5605                 if(this.filterOptRe.test(e)){
5606                     continue;
5607                 }
5608                 if(typeof o[e] == "function"){
5609                     // shared options
5610                     this.addListener(e, o[e], o.scope,  o);
5611                 }else{
5612                     // individual options
5613                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5614                 }
5615             }
5616             return;
5617         }
5618         o = (!o || typeof o == "boolean") ? {} : o;
5619         eventName = eventName.toLowerCase();
5620         var ce = this.events[eventName] || true;
5621         if(typeof ce == "boolean"){
5622             ce = new Roo.util.Event(this, eventName);
5623             this.events[eventName] = ce;
5624         }
5625         ce.addListener(fn, scope, o);
5626     },
5627
5628     /**
5629      * Removes a listener
5630      * @param {String}   eventName     The type of event to listen for
5631      * @param {Function} handler        The handler to remove
5632      * @param {Object}   scope  (optional) The scope (this object) for the handler
5633      */
5634     removeListener : function(eventName, fn, scope){
5635         var ce = this.events[eventName.toLowerCase()];
5636         if(typeof ce == "object"){
5637             ce.removeListener(fn, scope);
5638         }
5639     },
5640
5641     /**
5642      * Removes all listeners for this object
5643      */
5644     purgeListeners : function(){
5645         for(var evt in this.events){
5646             if(typeof this.events[evt] == "object"){
5647                  this.events[evt].clearListeners();
5648             }
5649         }
5650     },
5651
5652     relayEvents : function(o, events){
5653         var createHandler = function(ename){
5654             return function(){
5655                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5656             };
5657         };
5658         for(var i = 0, len = events.length; i < len; i++){
5659             var ename = events[i];
5660             if(!this.events[ename]){ this.events[ename] = true; };
5661             o.on(ename, createHandler(ename), this);
5662         }
5663     },
5664
5665     /**
5666      * Used to define events on this Observable
5667      * @param {Object} object The object with the events defined
5668      */
5669     addEvents : function(o){
5670         if(!this.events){
5671             this.events = {};
5672         }
5673         Roo.applyIf(this.events, o);
5674     },
5675
5676     /**
5677      * Checks to see if this object has any listeners for a specified event
5678      * @param {String} eventName The name of the event to check for
5679      * @return {Boolean} True if the event is being listened for, else false
5680      */
5681     hasListener : function(eventName){
5682         var e = this.events[eventName];
5683         return typeof e == "object" && e.listeners.length > 0;
5684     }
5685 };
5686 /**
5687  * Appends an event handler to this element (shorthand for addListener)
5688  * @param {String}   eventName     The type of event to listen for
5689  * @param {Function} handler        The method the event invokes
5690  * @param {Object}   scope (optional) The scope in which to execute the handler
5691  * function. The handler function's "this" context.
5692  * @param {Object}   options  (optional)
5693  * @method
5694  */
5695 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5696 /**
5697  * Removes a listener (shorthand for removeListener)
5698  * @param {String}   eventName     The type of event to listen for
5699  * @param {Function} handler        The handler to remove
5700  * @param {Object}   scope  (optional) The scope (this object) for the handler
5701  * @method
5702  */
5703 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5704
5705 /**
5706  * Starts capture on the specified Observable. All events will be passed
5707  * to the supplied function with the event name + standard signature of the event
5708  * <b>before</b> the event is fired. If the supplied function returns false,
5709  * the event will not fire.
5710  * @param {Observable} o The Observable to capture
5711  * @param {Function} fn The function to call
5712  * @param {Object} scope (optional) The scope (this object) for the fn
5713  * @static
5714  */
5715 Roo.util.Observable.capture = function(o, fn, scope){
5716     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5717 };
5718
5719 /**
5720  * Removes <b>all</b> added captures from the Observable.
5721  * @param {Observable} o The Observable to release
5722  * @static
5723  */
5724 Roo.util.Observable.releaseCapture = function(o){
5725     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5726 };
5727
5728 (function(){
5729
5730     var createBuffered = function(h, o, scope){
5731         var task = new Roo.util.DelayedTask();
5732         return function(){
5733             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5734         };
5735     };
5736
5737     var createSingle = function(h, e, fn, scope){
5738         return function(){
5739             e.removeListener(fn, scope);
5740             return h.apply(scope, arguments);
5741         };
5742     };
5743
5744     var createDelayed = function(h, o, scope){
5745         return function(){
5746             var args = Array.prototype.slice.call(arguments, 0);
5747             setTimeout(function(){
5748                 h.apply(scope, args);
5749             }, o.delay || 10);
5750         };
5751     };
5752
5753     Roo.util.Event = function(obj, name){
5754         this.name = name;
5755         this.obj = obj;
5756         this.listeners = [];
5757     };
5758
5759     Roo.util.Event.prototype = {
5760         addListener : function(fn, scope, options){
5761             var o = options || {};
5762             scope = scope || this.obj;
5763             if(!this.isListening(fn, scope)){
5764                 var l = {fn: fn, scope: scope, options: o};
5765                 var h = fn;
5766                 if(o.delay){
5767                     h = createDelayed(h, o, scope);
5768                 }
5769                 if(o.single){
5770                     h = createSingle(h, this, fn, scope);
5771                 }
5772                 if(o.buffer){
5773                     h = createBuffered(h, o, scope);
5774                 }
5775                 l.fireFn = h;
5776                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5777                     this.listeners.push(l);
5778                 }else{
5779                     this.listeners = this.listeners.slice(0);
5780                     this.listeners.push(l);
5781                 }
5782             }
5783         },
5784
5785         findListener : function(fn, scope){
5786             scope = scope || this.obj;
5787             var ls = this.listeners;
5788             for(var i = 0, len = ls.length; i < len; i++){
5789                 var l = ls[i];
5790                 if(l.fn == fn && l.scope == scope){
5791                     return i;
5792                 }
5793             }
5794             return -1;
5795         },
5796
5797         isListening : function(fn, scope){
5798             return this.findListener(fn, scope) != -1;
5799         },
5800
5801         removeListener : function(fn, scope){
5802             var index;
5803             if((index = this.findListener(fn, scope)) != -1){
5804                 if(!this.firing){
5805                     this.listeners.splice(index, 1);
5806                 }else{
5807                     this.listeners = this.listeners.slice(0);
5808                     this.listeners.splice(index, 1);
5809                 }
5810                 return true;
5811             }
5812             return false;
5813         },
5814
5815         clearListeners : function(){
5816             this.listeners = [];
5817         },
5818
5819         fire : function(){
5820             var ls = this.listeners, scope, len = ls.length;
5821             if(len > 0){
5822                 this.firing = true;
5823                 var args = Array.prototype.slice.call(arguments, 0);
5824                 for(var i = 0; i < len; i++){
5825                     var l = ls[i];
5826                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5827                         this.firing = false;
5828                         return false;
5829                     }
5830                 }
5831                 this.firing = false;
5832             }
5833             return true;
5834         }
5835     };
5836 })();/*
5837  * Based on:
5838  * Ext JS Library 1.1.1
5839  * Copyright(c) 2006-2007, Ext JS, LLC.
5840  *
5841  * Originally Released Under LGPL - original licence link has changed is not relivant.
5842  *
5843  * Fork - LGPL
5844  * <script type="text/javascript">
5845  */
5846
5847 /**
5848  * @class Roo.EventManager
5849  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5850  * several useful events directly.
5851  * See {@link Roo.EventObject} for more details on normalized event objects.
5852  * @singleton
5853  */
5854 Roo.EventManager = function(){
5855     var docReadyEvent, docReadyProcId, docReadyState = false;
5856     var resizeEvent, resizeTask, textEvent, textSize;
5857     var E = Roo.lib.Event;
5858     var D = Roo.lib.Dom;
5859
5860
5861     var fireDocReady = function(){
5862         if(!docReadyState){
5863             docReadyState = true;
5864             Roo.isReady = true;
5865             if(docReadyProcId){
5866                 clearInterval(docReadyProcId);
5867             }
5868             if(Roo.isGecko || Roo.isOpera) {
5869                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5870             }
5871             if(Roo.isIE){
5872                 var defer = document.getElementById("ie-deferred-loader");
5873                 if(defer){
5874                     defer.onreadystatechange = null;
5875                     defer.parentNode.removeChild(defer);
5876                 }
5877             }
5878             if(docReadyEvent){
5879                 docReadyEvent.fire();
5880                 docReadyEvent.clearListeners();
5881             }
5882         }
5883     };
5884     
5885     var initDocReady = function(){
5886         docReadyEvent = new Roo.util.Event();
5887         if(Roo.isGecko || Roo.isOpera) {
5888             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5889         }else if(Roo.isIE){
5890             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5891             var defer = document.getElementById("ie-deferred-loader");
5892             defer.onreadystatechange = function(){
5893                 if(this.readyState == "complete"){
5894                     fireDocReady();
5895                 }
5896             };
5897         }else if(Roo.isSafari){ 
5898             docReadyProcId = setInterval(function(){
5899                 var rs = document.readyState;
5900                 if(rs == "complete") {
5901                     fireDocReady();     
5902                  }
5903             }, 10);
5904         }
5905         // no matter what, make sure it fires on load
5906         E.on(window, "load", fireDocReady);
5907     };
5908
5909     var createBuffered = function(h, o){
5910         var task = new Roo.util.DelayedTask(h);
5911         return function(e){
5912             // create new event object impl so new events don't wipe out properties
5913             e = new Roo.EventObjectImpl(e);
5914             task.delay(o.buffer, h, null, [e]);
5915         };
5916     };
5917
5918     var createSingle = function(h, el, ename, fn){
5919         return function(e){
5920             Roo.EventManager.removeListener(el, ename, fn);
5921             h(e);
5922         };
5923     };
5924
5925     var createDelayed = function(h, o){
5926         return function(e){
5927             // create new event object impl so new events don't wipe out properties
5928             e = new Roo.EventObjectImpl(e);
5929             setTimeout(function(){
5930                 h(e);
5931             }, o.delay || 10);
5932         };
5933     };
5934
5935     var listen = function(element, ename, opt, fn, scope){
5936         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5937         fn = fn || o.fn; scope = scope || o.scope;
5938         var el = Roo.getDom(element);
5939         if(!el){
5940             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5941         }
5942         var h = function(e){
5943             e = Roo.EventObject.setEvent(e);
5944             var t;
5945             if(o.delegate){
5946                 t = e.getTarget(o.delegate, el);
5947                 if(!t){
5948                     return;
5949                 }
5950             }else{
5951                 t = e.target;
5952             }
5953             if(o.stopEvent === true){
5954                 e.stopEvent();
5955             }
5956             if(o.preventDefault === true){
5957                e.preventDefault();
5958             }
5959             if(o.stopPropagation === true){
5960                 e.stopPropagation();
5961             }
5962
5963             if(o.normalized === false){
5964                 e = e.browserEvent;
5965             }
5966
5967             fn.call(scope || el, e, t, o);
5968         };
5969         if(o.delay){
5970             h = createDelayed(h, o);
5971         }
5972         if(o.single){
5973             h = createSingle(h, el, ename, fn);
5974         }
5975         if(o.buffer){
5976             h = createBuffered(h, o);
5977         }
5978         fn._handlers = fn._handlers || [];
5979         fn._handlers.push([Roo.id(el), ename, h]);
5980
5981         E.on(el, ename, h);
5982         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5983             el.addEventListener("DOMMouseScroll", h, false);
5984             E.on(window, 'unload', function(){
5985                 el.removeEventListener("DOMMouseScroll", h, false);
5986             });
5987         }
5988         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5989             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5990         }
5991         return h;
5992     };
5993
5994     var stopListening = function(el, ename, fn){
5995         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5996         if(hds){
5997             for(var i = 0, len = hds.length; i < len; i++){
5998                 var h = hds[i];
5999                 if(h[0] == id && h[1] == ename){
6000                     hd = h[2];
6001                     hds.splice(i, 1);
6002                     break;
6003                 }
6004             }
6005         }
6006         E.un(el, ename, hd);
6007         el = Roo.getDom(el);
6008         if(ename == "mousewheel" && el.addEventListener){
6009             el.removeEventListener("DOMMouseScroll", hd, false);
6010         }
6011         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6012             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6013         }
6014     };
6015
6016     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6017     
6018     var pub = {
6019         
6020         
6021         /** 
6022          * Fix for doc tools
6023          * @scope Roo.EventManager
6024          */
6025         
6026         
6027         /** 
6028          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6029          * object with a Roo.EventObject
6030          * @param {Function} fn        The method the event invokes
6031          * @param {Object}   scope    An object that becomes the scope of the handler
6032          * @param {boolean}  override If true, the obj passed in becomes
6033          *                             the execution scope of the listener
6034          * @return {Function} The wrapped function
6035          * @deprecated
6036          */
6037         wrap : function(fn, scope, override){
6038             return function(e){
6039                 Roo.EventObject.setEvent(e);
6040                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6041             };
6042         },
6043         
6044         /**
6045      * Appends an event handler to an element (shorthand for addListener)
6046      * @param {String/HTMLElement}   element        The html element or id to assign the
6047      * @param {String}   eventName The type of event to listen for
6048      * @param {Function} handler The method the event invokes
6049      * @param {Object}   scope (optional) The scope in which to execute the handler
6050      * function. The handler function's "this" context.
6051      * @param {Object}   options (optional) An object containing handler configuration
6052      * properties. This may contain any of the following properties:<ul>
6053      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6054      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6055      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6056      * <li>preventDefault {Boolean} True to prevent the default action</li>
6057      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6058      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6059      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6060      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6061      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6062      * by the specified number of milliseconds. If the event fires again within that time, the original
6063      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6064      * </ul><br>
6065      * <p>
6066      * <b>Combining Options</b><br>
6067      * Using the options argument, it is possible to combine different types of listeners:<br>
6068      * <br>
6069      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6070      * Code:<pre><code>
6071 el.on('click', this.onClick, this, {
6072     single: true,
6073     delay: 100,
6074     stopEvent : true,
6075     forumId: 4
6076 });</code></pre>
6077      * <p>
6078      * <b>Attaching multiple handlers in 1 call</b><br>
6079       * The method also allows for a single argument to be passed which is a config object containing properties
6080      * which specify multiple handlers.
6081      * <p>
6082      * Code:<pre><code>
6083 el.on({
6084     'click' : {
6085         fn: this.onClick
6086         scope: this,
6087         delay: 100
6088     },
6089     'mouseover' : {
6090         fn: this.onMouseOver
6091         scope: this
6092     },
6093     'mouseout' : {
6094         fn: this.onMouseOut
6095         scope: this
6096     }
6097 });</code></pre>
6098      * <p>
6099      * Or a shorthand syntax:<br>
6100      * Code:<pre><code>
6101 el.on({
6102     'click' : this.onClick,
6103     'mouseover' : this.onMouseOver,
6104     'mouseout' : this.onMouseOut
6105     scope: this
6106 });</code></pre>
6107      */
6108         addListener : function(element, eventName, fn, scope, options){
6109             if(typeof eventName == "object"){
6110                 var o = eventName;
6111                 for(var e in o){
6112                     if(propRe.test(e)){
6113                         continue;
6114                     }
6115                     if(typeof o[e] == "function"){
6116                         // shared options
6117                         listen(element, e, o, o[e], o.scope);
6118                     }else{
6119                         // individual options
6120                         listen(element, e, o[e]);
6121                     }
6122                 }
6123                 return;
6124             }
6125             return listen(element, eventName, options, fn, scope);
6126         },
6127         
6128         /**
6129          * Removes an event handler
6130          *
6131          * @param {String/HTMLElement}   element        The id or html element to remove the 
6132          *                             event from
6133          * @param {String}   eventName     The type of event
6134          * @param {Function} fn
6135          * @return {Boolean} True if a listener was actually removed
6136          */
6137         removeListener : function(element, eventName, fn){
6138             return stopListening(element, eventName, fn);
6139         },
6140         
6141         /**
6142          * Fires when the document is ready (before onload and before images are loaded). Can be 
6143          * accessed shorthanded Roo.onReady().
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An  object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onDocumentReady : function(fn, scope, options){
6149             if(docReadyState){ // if it already fired
6150                 docReadyEvent.addListener(fn, scope, options);
6151                 docReadyEvent.fire();
6152                 docReadyEvent.clearListeners();
6153                 return;
6154             }
6155             if(!docReadyEvent){
6156                 initDocReady();
6157             }
6158             docReadyEvent.addListener(fn, scope, options);
6159         },
6160         
6161         /**
6162          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6163          * @param {Function} fn        The method the event invokes
6164          * @param {Object}   scope    An object that becomes the scope of the handler
6165          * @param {boolean}  options
6166          */
6167         onWindowResize : function(fn, scope, options){
6168             if(!resizeEvent){
6169                 resizeEvent = new Roo.util.Event();
6170                 resizeTask = new Roo.util.DelayedTask(function(){
6171                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6172                 });
6173                 E.on(window, "resize", function(){
6174                     if(Roo.isIE){
6175                         resizeTask.delay(50);
6176                     }else{
6177                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6178                     }
6179                 });
6180             }
6181             resizeEvent.addListener(fn, scope, options);
6182         },
6183
6184         /**
6185          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6186          * @param {Function} fn        The method the event invokes
6187          * @param {Object}   scope    An object that becomes the scope of the handler
6188          * @param {boolean}  options
6189          */
6190         onTextResize : function(fn, scope, options){
6191             if(!textEvent){
6192                 textEvent = new Roo.util.Event();
6193                 var textEl = new Roo.Element(document.createElement('div'));
6194                 textEl.dom.className = 'x-text-resize';
6195                 textEl.dom.innerHTML = 'X';
6196                 textEl.appendTo(document.body);
6197                 textSize = textEl.dom.offsetHeight;
6198                 setInterval(function(){
6199                     if(textEl.dom.offsetHeight != textSize){
6200                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6201                     }
6202                 }, this.textResizeInterval);
6203             }
6204             textEvent.addListener(fn, scope, options);
6205         },
6206
6207         /**
6208          * Removes the passed window resize listener.
6209          * @param {Function} fn        The method the event invokes
6210          * @param {Object}   scope    The scope of handler
6211          */
6212         removeResizeListener : function(fn, scope){
6213             if(resizeEvent){
6214                 resizeEvent.removeListener(fn, scope);
6215             }
6216         },
6217
6218         // private
6219         fireResize : function(){
6220             if(resizeEvent){
6221                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6222             }   
6223         },
6224         /**
6225          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6226          */
6227         ieDeferSrc : false,
6228         /**
6229          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6230          */
6231         textResizeInterval : 50
6232     };
6233     
6234     /**
6235      * Fix for doc tools
6236      * @scopeAlias pub=Roo.EventManager
6237      */
6238     
6239      /**
6240      * Appends an event handler to an element (shorthand for addListener)
6241      * @param {String/HTMLElement}   element        The html element or id to assign the
6242      * @param {String}   eventName The type of event to listen for
6243      * @param {Function} handler The method the event invokes
6244      * @param {Object}   scope (optional) The scope in which to execute the handler
6245      * function. The handler function's "this" context.
6246      * @param {Object}   options (optional) An object containing handler configuration
6247      * properties. This may contain any of the following properties:<ul>
6248      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6249      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6250      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6251      * <li>preventDefault {Boolean} True to prevent the default action</li>
6252      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6253      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6254      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6255      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6256      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6257      * by the specified number of milliseconds. If the event fires again within that time, the original
6258      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6259      * </ul><br>
6260      * <p>
6261      * <b>Combining Options</b><br>
6262      * Using the options argument, it is possible to combine different types of listeners:<br>
6263      * <br>
6264      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6265      * Code:<pre><code>
6266 el.on('click', this.onClick, this, {
6267     single: true,
6268     delay: 100,
6269     stopEvent : true,
6270     forumId: 4
6271 });</code></pre>
6272      * <p>
6273      * <b>Attaching multiple handlers in 1 call</b><br>
6274       * The method also allows for a single argument to be passed which is a config object containing properties
6275      * which specify multiple handlers.
6276      * <p>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : {
6280         fn: this.onClick
6281         scope: this,
6282         delay: 100
6283     },
6284     'mouseover' : {
6285         fn: this.onMouseOver
6286         scope: this
6287     },
6288     'mouseout' : {
6289         fn: this.onMouseOut
6290         scope: this
6291     }
6292 });</code></pre>
6293      * <p>
6294      * Or a shorthand syntax:<br>
6295      * Code:<pre><code>
6296 el.on({
6297     'click' : this.onClick,
6298     'mouseover' : this.onMouseOver,
6299     'mouseout' : this.onMouseOut
6300     scope: this
6301 });</code></pre>
6302      */
6303     pub.on = pub.addListener;
6304     pub.un = pub.removeListener;
6305
6306     pub.stoppedMouseDownEvent = new Roo.util.Event();
6307     return pub;
6308 }();
6309 /**
6310   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6311   * @param {Function} fn        The method the event invokes
6312   * @param {Object}   scope    An  object that becomes the scope of the handler
6313   * @param {boolean}  override If true, the obj passed in becomes
6314   *                             the execution scope of the listener
6315   * @member Roo
6316   * @method onReady
6317  */
6318 Roo.onReady = Roo.EventManager.onDocumentReady;
6319
6320 Roo.onReady(function(){
6321     var bd = Roo.get(document.body);
6322     if(!bd){ return; }
6323
6324     var cls = [
6325             Roo.isIE ? "roo-ie"
6326             : Roo.isGecko ? "roo-gecko"
6327             : Roo.isOpera ? "roo-opera"
6328             : Roo.isSafari ? "roo-safari" : ""];
6329
6330     if(Roo.isMac){
6331         cls.push("roo-mac");
6332     }
6333     if(Roo.isLinux){
6334         cls.push("roo-linux");
6335     }
6336     if(Roo.isBorderBox){
6337         cls.push('roo-border-box');
6338     }
6339     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6340         var p = bd.dom.parentNode;
6341         if(p){
6342             p.className += ' roo-strict';
6343         }
6344     }
6345     bd.addClass(cls.join(' '));
6346 });
6347
6348 /**
6349  * @class Roo.EventObject
6350  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6351  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6352  * Example:
6353  * <pre><code>
6354  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6355     e.preventDefault();
6356     var target = e.getTarget();
6357     ...
6358  }
6359  var myDiv = Roo.get("myDiv");
6360  myDiv.on("click", handleClick);
6361  //or
6362  Roo.EventManager.on("myDiv", 'click', handleClick);
6363  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6364  </code></pre>
6365  * @singleton
6366  */
6367 Roo.EventObject = function(){
6368     
6369     var E = Roo.lib.Event;
6370     
6371     // safari keypress events for special keys return bad keycodes
6372     var safariKeys = {
6373         63234 : 37, // left
6374         63235 : 39, // right
6375         63232 : 38, // up
6376         63233 : 40, // down
6377         63276 : 33, // page up
6378         63277 : 34, // page down
6379         63272 : 46, // delete
6380         63273 : 36, // home
6381         63275 : 35  // end
6382     };
6383
6384     // normalize button clicks
6385     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6386                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6387
6388     Roo.EventObjectImpl = function(e){
6389         if(e){
6390             this.setEvent(e.browserEvent || e);
6391         }
6392     };
6393     Roo.EventObjectImpl.prototype = {
6394         /**
6395          * Used to fix doc tools.
6396          * @scope Roo.EventObject.prototype
6397          */
6398             
6399
6400         
6401         
6402         /** The normal browser event */
6403         browserEvent : null,
6404         /** The button pressed in a mouse event */
6405         button : -1,
6406         /** True if the shift key was down during the event */
6407         shiftKey : false,
6408         /** True if the control key was down during the event */
6409         ctrlKey : false,
6410         /** True if the alt key was down during the event */
6411         altKey : false,
6412
6413         /** Key constant 
6414         * @type Number */
6415         BACKSPACE : 8,
6416         /** Key constant 
6417         * @type Number */
6418         TAB : 9,
6419         /** Key constant 
6420         * @type Number */
6421         RETURN : 13,
6422         /** Key constant 
6423         * @type Number */
6424         ENTER : 13,
6425         /** Key constant 
6426         * @type Number */
6427         SHIFT : 16,
6428         /** Key constant 
6429         * @type Number */
6430         CONTROL : 17,
6431         /** Key constant 
6432         * @type Number */
6433         ESC : 27,
6434         /** Key constant 
6435         * @type Number */
6436         SPACE : 32,
6437         /** Key constant 
6438         * @type Number */
6439         PAGEUP : 33,
6440         /** Key constant 
6441         * @type Number */
6442         PAGEDOWN : 34,
6443         /** Key constant 
6444         * @type Number */
6445         END : 35,
6446         /** Key constant 
6447         * @type Number */
6448         HOME : 36,
6449         /** Key constant 
6450         * @type Number */
6451         LEFT : 37,
6452         /** Key constant 
6453         * @type Number */
6454         UP : 38,
6455         /** Key constant 
6456         * @type Number */
6457         RIGHT : 39,
6458         /** Key constant 
6459         * @type Number */
6460         DOWN : 40,
6461         /** Key constant 
6462         * @type Number */
6463         DELETE : 46,
6464         /** Key constant 
6465         * @type Number */
6466         F5 : 116,
6467
6468            /** @private */
6469         setEvent : function(e){
6470             if(e == this || (e && e.browserEvent)){ // already wrapped
6471                 return e;
6472             }
6473             this.browserEvent = e;
6474             if(e){
6475                 // normalize buttons
6476                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6477                 if(e.type == 'click' && this.button == -1){
6478                     this.button = 0;
6479                 }
6480                 this.type = e.type;
6481                 this.shiftKey = e.shiftKey;
6482                 // mac metaKey behaves like ctrlKey
6483                 this.ctrlKey = e.ctrlKey || e.metaKey;
6484                 this.altKey = e.altKey;
6485                 // in getKey these will be normalized for the mac
6486                 this.keyCode = e.keyCode;
6487                 // keyup warnings on firefox.
6488                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6489                 // cache the target for the delayed and or buffered events
6490                 this.target = E.getTarget(e);
6491                 // same for XY
6492                 this.xy = E.getXY(e);
6493             }else{
6494                 this.button = -1;
6495                 this.shiftKey = false;
6496                 this.ctrlKey = false;
6497                 this.altKey = false;
6498                 this.keyCode = 0;
6499                 this.charCode =0;
6500                 this.target = null;
6501                 this.xy = [0, 0];
6502             }
6503             return this;
6504         },
6505
6506         /**
6507          * Stop the event (preventDefault and stopPropagation)
6508          */
6509         stopEvent : function(){
6510             if(this.browserEvent){
6511                 if(this.browserEvent.type == 'mousedown'){
6512                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6513                 }
6514                 E.stopEvent(this.browserEvent);
6515             }
6516         },
6517
6518         /**
6519          * Prevents the browsers default handling of the event.
6520          */
6521         preventDefault : function(){
6522             if(this.browserEvent){
6523                 E.preventDefault(this.browserEvent);
6524             }
6525         },
6526
6527         /** @private */
6528         isNavKeyPress : function(){
6529             var k = this.keyCode;
6530             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6531             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6532         },
6533
6534         isSpecialKey : function(){
6535             var k = this.keyCode;
6536             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6537             (k == 16) || (k == 17) ||
6538             (k >= 18 && k <= 20) ||
6539             (k >= 33 && k <= 35) ||
6540             (k >= 36 && k <= 39) ||
6541             (k >= 44 && k <= 45);
6542         },
6543         /**
6544          * Cancels bubbling of the event.
6545          */
6546         stopPropagation : function(){
6547             if(this.browserEvent){
6548                 if(this.type == 'mousedown'){
6549                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6550                 }
6551                 E.stopPropagation(this.browserEvent);
6552             }
6553         },
6554
6555         /**
6556          * Gets the key code for the event.
6557          * @return {Number}
6558          */
6559         getCharCode : function(){
6560             return this.charCode || this.keyCode;
6561         },
6562
6563         /**
6564          * Returns a normalized keyCode for the event.
6565          * @return {Number} The key code
6566          */
6567         getKey : function(){
6568             var k = this.keyCode || this.charCode;
6569             return Roo.isSafari ? (safariKeys[k] || k) : k;
6570         },
6571
6572         /**
6573          * Gets the x coordinate of the event.
6574          * @return {Number}
6575          */
6576         getPageX : function(){
6577             return this.xy[0];
6578         },
6579
6580         /**
6581          * Gets the y coordinate of the event.
6582          * @return {Number}
6583          */
6584         getPageY : function(){
6585             return this.xy[1];
6586         },
6587
6588         /**
6589          * Gets the time of the event.
6590          * @return {Number}
6591          */
6592         getTime : function(){
6593             if(this.browserEvent){
6594                 return E.getTime(this.browserEvent);
6595             }
6596             return null;
6597         },
6598
6599         /**
6600          * Gets the page coordinates of the event.
6601          * @return {Array} The xy values like [x, y]
6602          */
6603         getXY : function(){
6604             return this.xy;
6605         },
6606
6607         /**
6608          * Gets the target for the event.
6609          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6610          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6611                 search as a number or element (defaults to 10 || document.body)
6612          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6613          * @return {HTMLelement}
6614          */
6615         getTarget : function(selector, maxDepth, returnEl){
6616             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6617         },
6618         /**
6619          * Gets the related target.
6620          * @return {HTMLElement}
6621          */
6622         getRelatedTarget : function(){
6623             if(this.browserEvent){
6624                 return E.getRelatedTarget(this.browserEvent);
6625             }
6626             return null;
6627         },
6628
6629         /**
6630          * Normalizes mouse wheel delta across browsers
6631          * @return {Number} The delta
6632          */
6633         getWheelDelta : function(){
6634             var e = this.browserEvent;
6635             var delta = 0;
6636             if(e.wheelDelta){ /* IE/Opera. */
6637                 delta = e.wheelDelta/120;
6638             }else if(e.detail){ /* Mozilla case. */
6639                 delta = -e.detail/3;
6640             }
6641             return delta;
6642         },
6643
6644         /**
6645          * Returns true if the control, meta, shift or alt key was pressed during this event.
6646          * @return {Boolean}
6647          */
6648         hasModifier : function(){
6649             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6650         },
6651
6652         /**
6653          * Returns true if the target of this event equals el or is a child of el
6654          * @param {String/HTMLElement/Element} el
6655          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6656          * @return {Boolean}
6657          */
6658         within : function(el, related){
6659             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6660             return t && Roo.fly(el).contains(t);
6661         },
6662
6663         getPoint : function(){
6664             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6665         }
6666     };
6667
6668     return new Roo.EventObjectImpl();
6669 }();
6670             
6671     /*
6672  * Based on:
6673  * Ext JS Library 1.1.1
6674  * Copyright(c) 2006-2007, Ext JS, LLC.
6675  *
6676  * Originally Released Under LGPL - original licence link has changed is not relivant.
6677  *
6678  * Fork - LGPL
6679  * <script type="text/javascript">
6680  */
6681
6682  
6683 // was in Composite Element!??!?!
6684  
6685 (function(){
6686     var D = Roo.lib.Dom;
6687     var E = Roo.lib.Event;
6688     var A = Roo.lib.Anim;
6689
6690     // local style camelizing for speed
6691     var propCache = {};
6692     var camelRe = /(-[a-z])/gi;
6693     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6694     var view = document.defaultView;
6695
6696 /**
6697  * @class Roo.Element
6698  * Represents an Element in the DOM.<br><br>
6699  * Usage:<br>
6700 <pre><code>
6701 var el = Roo.get("my-div");
6702
6703 // or with getEl
6704 var el = getEl("my-div");
6705
6706 // or with a DOM element
6707 var el = Roo.get(myDivElement);
6708 </code></pre>
6709  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6710  * each call instead of constructing a new one.<br><br>
6711  * <b>Animations</b><br />
6712  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6713  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6714 <pre>
6715 Option    Default   Description
6716 --------- --------  ---------------------------------------------
6717 duration  .35       The duration of the animation in seconds
6718 easing    easeOut   The YUI easing method
6719 callback  none      A function to execute when the anim completes
6720 scope     this      The scope (this) of the callback function
6721 </pre>
6722 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6723 * manipulate the animation. Here's an example:
6724 <pre><code>
6725 var el = Roo.get("my-div");
6726
6727 // no animation
6728 el.setWidth(100);
6729
6730 // default animation
6731 el.setWidth(100, true);
6732
6733 // animation with some options set
6734 el.setWidth(100, {
6735     duration: 1,
6736     callback: this.foo,
6737     scope: this
6738 });
6739
6740 // using the "anim" property to get the Anim object
6741 var opt = {
6742     duration: 1,
6743     callback: this.foo,
6744     scope: this
6745 };
6746 el.setWidth(100, opt);
6747 ...
6748 if(opt.anim.isAnimated()){
6749     opt.anim.stop();
6750 }
6751 </code></pre>
6752 * <b> Composite (Collections of) Elements</b><br />
6753  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6754  * @constructor Create a new Element directly.
6755  * @param {String/HTMLElement} element
6756  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6757  */
6758     Roo.Element = function(element, forceNew){
6759         var dom = typeof element == "string" ?
6760                 document.getElementById(element) : element;
6761         if(!dom){ // invalid id/element
6762             return null;
6763         }
6764         var id = dom.id;
6765         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6766             return Roo.Element.cache[id];
6767         }
6768
6769         /**
6770          * The DOM element
6771          * @type HTMLElement
6772          */
6773         this.dom = dom;
6774
6775         /**
6776          * The DOM element ID
6777          * @type String
6778          */
6779         this.id = id || Roo.id(dom);
6780     };
6781
6782     var El = Roo.Element;
6783
6784     El.prototype = {
6785         /**
6786          * The element's default display mode  (defaults to "")
6787          * @type String
6788          */
6789         originalDisplay : "",
6790
6791         visibilityMode : 1,
6792         /**
6793          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6794          * @type String
6795          */
6796         defaultUnit : "px",
6797         /**
6798          * Sets the element's visibility mode. When setVisible() is called it
6799          * will use this to determine whether to set the visibility or the display property.
6800          * @param visMode Element.VISIBILITY or Element.DISPLAY
6801          * @return {Roo.Element} this
6802          */
6803         setVisibilityMode : function(visMode){
6804             this.visibilityMode = visMode;
6805             return this;
6806         },
6807         /**
6808          * Convenience method for setVisibilityMode(Element.DISPLAY)
6809          * @param {String} display (optional) What to set display to when visible
6810          * @return {Roo.Element} this
6811          */
6812         enableDisplayMode : function(display){
6813             this.setVisibilityMode(El.DISPLAY);
6814             if(typeof display != "undefined") this.originalDisplay = display;
6815             return this;
6816         },
6817
6818         /**
6819          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6820          * @param {String} selector The simple selector to test
6821          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6822                 search as a number or element (defaults to 10 || document.body)
6823          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6824          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6825          */
6826         findParent : function(simpleSelector, maxDepth, returnEl){
6827             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6828             maxDepth = maxDepth || 50;
6829             if(typeof maxDepth != "number"){
6830                 stopEl = Roo.getDom(maxDepth);
6831                 maxDepth = 10;
6832             }
6833             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6834                 if(dq.is(p, simpleSelector)){
6835                     return returnEl ? Roo.get(p) : p;
6836                 }
6837                 depth++;
6838                 p = p.parentNode;
6839             }
6840             return null;
6841         },
6842
6843
6844         /**
6845          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6846          * @param {String} selector The simple selector to test
6847          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6848                 search as a number or element (defaults to 10 || document.body)
6849          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6850          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6851          */
6852         findParentNode : function(simpleSelector, maxDepth, returnEl){
6853             var p = Roo.fly(this.dom.parentNode, '_internal');
6854             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6855         },
6856
6857         /**
6858          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6859          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6860          * @param {String} selector The simple selector to test
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6864          */
6865         up : function(simpleSelector, maxDepth){
6866             return this.findParentNode(simpleSelector, maxDepth, true);
6867         },
6868
6869
6870
6871         /**
6872          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6873          * @param {String} selector The simple selector to test
6874          * @return {Boolean} True if this element matches the selector, else false
6875          */
6876         is : function(simpleSelector){
6877             return Roo.DomQuery.is(this.dom, simpleSelector);
6878         },
6879
6880         /**
6881          * Perform animation on this element.
6882          * @param {Object} args The YUI animation control args
6883          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6884          * @param {Function} onComplete (optional) Function to call when animation completes
6885          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6886          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6887          * @return {Roo.Element} this
6888          */
6889         animate : function(args, duration, onComplete, easing, animType){
6890             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6891             return this;
6892         },
6893
6894         /*
6895          * @private Internal animation call
6896          */
6897         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6898             animType = animType || 'run';
6899             opt = opt || {};
6900             var anim = Roo.lib.Anim[animType](
6901                 this.dom, args,
6902                 (opt.duration || defaultDur) || .35,
6903                 (opt.easing || defaultEase) || 'easeOut',
6904                 function(){
6905                     Roo.callback(cb, this);
6906                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6907                 },
6908                 this
6909             );
6910             opt.anim = anim;
6911             return anim;
6912         },
6913
6914         // private legacy anim prep
6915         preanim : function(a, i){
6916             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6917         },
6918
6919         /**
6920          * Removes worthless text nodes
6921          * @param {Boolean} forceReclean (optional) By default the element
6922          * keeps track if it has been cleaned already so
6923          * you can call this over and over. However, if you update the element and
6924          * need to force a reclean, you can pass true.
6925          */
6926         clean : function(forceReclean){
6927             if(this.isCleaned && forceReclean !== true){
6928                 return this;
6929             }
6930             var ns = /\S/;
6931             var d = this.dom, n = d.firstChild, ni = -1;
6932             while(n){
6933                 var nx = n.nextSibling;
6934                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6935                     d.removeChild(n);
6936                 }else{
6937                     n.nodeIndex = ++ni;
6938                 }
6939                 n = nx;
6940             }
6941             this.isCleaned = true;
6942             return this;
6943         },
6944
6945         // private
6946         calcOffsetsTo : function(el){
6947             el = Roo.get(el);
6948             var d = el.dom;
6949             var restorePos = false;
6950             if(el.getStyle('position') == 'static'){
6951                 el.position('relative');
6952                 restorePos = true;
6953             }
6954             var x = 0, y =0;
6955             var op = this.dom;
6956             while(op && op != d && op.tagName != 'HTML'){
6957                 x+= op.offsetLeft;
6958                 y+= op.offsetTop;
6959                 op = op.offsetParent;
6960             }
6961             if(restorePos){
6962                 el.position('static');
6963             }
6964             return [x, y];
6965         },
6966
6967         /**
6968          * Scrolls this element into view within the passed container.
6969          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6970          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6971          * @return {Roo.Element} this
6972          */
6973         scrollIntoView : function(container, hscroll){
6974             var c = Roo.getDom(container) || document.body;
6975             var el = this.dom;
6976
6977             var o = this.calcOffsetsTo(c),
6978                 l = o[0],
6979                 t = o[1],
6980                 b = t+el.offsetHeight,
6981                 r = l+el.offsetWidth;
6982
6983             var ch = c.clientHeight;
6984             var ct = parseInt(c.scrollTop, 10);
6985             var cl = parseInt(c.scrollLeft, 10);
6986             var cb = ct + ch;
6987             var cr = cl + c.clientWidth;
6988
6989             if(t < ct){
6990                 c.scrollTop = t;
6991             }else if(b > cb){
6992                 c.scrollTop = b-ch;
6993             }
6994
6995             if(hscroll !== false){
6996                 if(l < cl){
6997                     c.scrollLeft = l;
6998                 }else if(r > cr){
6999                     c.scrollLeft = r-c.clientWidth;
7000                 }
7001             }
7002             return this;
7003         },
7004
7005         // private
7006         scrollChildIntoView : function(child, hscroll){
7007             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7008         },
7009
7010         /**
7011          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7012          * the new height may not be available immediately.
7013          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7014          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7015          * @param {Function} onComplete (optional) Function to call when animation completes
7016          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7017          * @return {Roo.Element} this
7018          */
7019         autoHeight : function(animate, duration, onComplete, easing){
7020             var oldHeight = this.getHeight();
7021             this.clip();
7022             this.setHeight(1); // force clipping
7023             setTimeout(function(){
7024                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7025                 if(!animate){
7026                     this.setHeight(height);
7027                     this.unclip();
7028                     if(typeof onComplete == "function"){
7029                         onComplete();
7030                     }
7031                 }else{
7032                     this.setHeight(oldHeight); // restore original height
7033                     this.setHeight(height, animate, duration, function(){
7034                         this.unclip();
7035                         if(typeof onComplete == "function") onComplete();
7036                     }.createDelegate(this), easing);
7037                 }
7038             }.createDelegate(this), 0);
7039             return this;
7040         },
7041
7042         /**
7043          * Returns true if this element is an ancestor of the passed element
7044          * @param {HTMLElement/String} el The element to check
7045          * @return {Boolean} True if this element is an ancestor of el, else false
7046          */
7047         contains : function(el){
7048             if(!el){return false;}
7049             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7050         },
7051
7052         /**
7053          * Checks whether the element is currently visible using both visibility and display properties.
7054          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7055          * @return {Boolean} True if the element is currently visible, else false
7056          */
7057         isVisible : function(deep) {
7058             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7059             if(deep !== true || !vis){
7060                 return vis;
7061             }
7062             var p = this.dom.parentNode;
7063             while(p && p.tagName.toLowerCase() != "body"){
7064                 if(!Roo.fly(p, '_isVisible').isVisible()){
7065                     return false;
7066                 }
7067                 p = p.parentNode;
7068             }
7069             return true;
7070         },
7071
7072         /**
7073          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7074          * @param {String} selector The CSS selector
7075          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7076          * @return {CompositeElement/CompositeElementLite} The composite element
7077          */
7078         select : function(selector, unique){
7079             return El.select(selector, unique, this.dom);
7080         },
7081
7082         /**
7083          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7084          * @param {String} selector The CSS selector
7085          * @return {Array} An array of the matched nodes
7086          */
7087         query : function(selector, unique){
7088             return Roo.DomQuery.select(selector, this.dom);
7089         },
7090
7091         /**
7092          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7093          * @param {String} selector The CSS selector
7094          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7095          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7096          */
7097         child : function(selector, returnDom){
7098             var n = Roo.DomQuery.selectNode(selector, this.dom);
7099             return returnDom ? n : Roo.get(n);
7100         },
7101
7102         /**
7103          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7104          * @param {String} selector The CSS selector
7105          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7106          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7107          */
7108         down : function(selector, returnDom){
7109             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7110             return returnDom ? n : Roo.get(n);
7111         },
7112
7113         /**
7114          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7115          * @param {String} group The group the DD object is member of
7116          * @param {Object} config The DD config object
7117          * @param {Object} overrides An object containing methods to override/implement on the DD object
7118          * @return {Roo.dd.DD} The DD object
7119          */
7120         initDD : function(group, config, overrides){
7121             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7122             return Roo.apply(dd, overrides);
7123         },
7124
7125         /**
7126          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7127          * @param {String} group The group the DDProxy object is member of
7128          * @param {Object} config The DDProxy config object
7129          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7130          * @return {Roo.dd.DDProxy} The DDProxy object
7131          */
7132         initDDProxy : function(group, config, overrides){
7133             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7134             return Roo.apply(dd, overrides);
7135         },
7136
7137         /**
7138          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7139          * @param {String} group The group the DDTarget object is member of
7140          * @param {Object} config The DDTarget config object
7141          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7142          * @return {Roo.dd.DDTarget} The DDTarget object
7143          */
7144         initDDTarget : function(group, config, overrides){
7145             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7146             return Roo.apply(dd, overrides);
7147         },
7148
7149         /**
7150          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7151          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7152          * @param {Boolean} visible Whether the element is visible
7153          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7154          * @return {Roo.Element} this
7155          */
7156          setVisible : function(visible, animate){
7157             if(!animate || !A){
7158                 if(this.visibilityMode == El.DISPLAY){
7159                     this.setDisplayed(visible);
7160                 }else{
7161                     this.fixDisplay();
7162                     this.dom.style.visibility = visible ? "visible" : "hidden";
7163                 }
7164             }else{
7165                 // closure for composites
7166                 var dom = this.dom;
7167                 var visMode = this.visibilityMode;
7168                 if(visible){
7169                     this.setOpacity(.01);
7170                     this.setVisible(true);
7171                 }
7172                 this.anim({opacity: { to: (visible?1:0) }},
7173                       this.preanim(arguments, 1),
7174                       null, .35, 'easeIn', function(){
7175                          if(!visible){
7176                              if(visMode == El.DISPLAY){
7177                                  dom.style.display = "none";
7178                              }else{
7179                                  dom.style.visibility = "hidden";
7180                              }
7181                              Roo.get(dom).setOpacity(1);
7182                          }
7183                      });
7184             }
7185             return this;
7186         },
7187
7188         /**
7189          * Returns true if display is not "none"
7190          * @return {Boolean}
7191          */
7192         isDisplayed : function() {
7193             return this.getStyle("display") != "none";
7194         },
7195
7196         /**
7197          * Toggles the element's visibility or display, depending on visibility mode.
7198          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7199          * @return {Roo.Element} this
7200          */
7201         toggle : function(animate){
7202             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7203             return this;
7204         },
7205
7206         /**
7207          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7208          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7209          * @return {Roo.Element} this
7210          */
7211         setDisplayed : function(value) {
7212             if(typeof value == "boolean"){
7213                value = value ? this.originalDisplay : "none";
7214             }
7215             this.setStyle("display", value);
7216             return this;
7217         },
7218
7219         /**
7220          * Tries to focus the element. Any exceptions are caught and ignored.
7221          * @return {Roo.Element} this
7222          */
7223         focus : function() {
7224             try{
7225                 this.dom.focus();
7226             }catch(e){}
7227             return this;
7228         },
7229
7230         /**
7231          * Tries to blur the element. Any exceptions are caught and ignored.
7232          * @return {Roo.Element} this
7233          */
7234         blur : function() {
7235             try{
7236                 this.dom.blur();
7237             }catch(e){}
7238             return this;
7239         },
7240
7241         /**
7242          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7243          * @param {String/Array} className The CSS class to add, or an array of classes
7244          * @return {Roo.Element} this
7245          */
7246         addClass : function(className){
7247             if(className instanceof Array){
7248                 for(var i = 0, len = className.length; i < len; i++) {
7249                     this.addClass(className[i]);
7250                 }
7251             }else{
7252                 if(className && !this.hasClass(className)){
7253                     this.dom.className = this.dom.className + " " + className;
7254                 }
7255             }
7256             return this;
7257         },
7258
7259         /**
7260          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7261          * @param {String/Array} className The CSS class to add, or an array of classes
7262          * @return {Roo.Element} this
7263          */
7264         radioClass : function(className){
7265             var siblings = this.dom.parentNode.childNodes;
7266             for(var i = 0; i < siblings.length; i++) {
7267                 var s = siblings[i];
7268                 if(s.nodeType == 1){
7269                     Roo.get(s).removeClass(className);
7270                 }
7271             }
7272             this.addClass(className);
7273             return this;
7274         },
7275
7276         /**
7277          * Removes one or more CSS classes from the element.
7278          * @param {String/Array} className The CSS class to remove, or an array of classes
7279          * @return {Roo.Element} this
7280          */
7281         removeClass : function(className){
7282             if(!className || !this.dom.className){
7283                 return this;
7284             }
7285             if(className instanceof Array){
7286                 for(var i = 0, len = className.length; i < len; i++) {
7287                     this.removeClass(className[i]);
7288                 }
7289             }else{
7290                 if(this.hasClass(className)){
7291                     var re = this.classReCache[className];
7292                     if (!re) {
7293                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7294                        this.classReCache[className] = re;
7295                     }
7296                     this.dom.className =
7297                         this.dom.className.replace(re, " ");
7298                 }
7299             }
7300             return this;
7301         },
7302
7303         // private
7304         classReCache: {},
7305
7306         /**
7307          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7308          * @param {String} className The CSS class to toggle
7309          * @return {Roo.Element} this
7310          */
7311         toggleClass : function(className){
7312             if(this.hasClass(className)){
7313                 this.removeClass(className);
7314             }else{
7315                 this.addClass(className);
7316             }
7317             return this;
7318         },
7319
7320         /**
7321          * Checks if the specified CSS class exists on this element's DOM node.
7322          * @param {String} className The CSS class to check for
7323          * @return {Boolean} True if the class exists, else false
7324          */
7325         hasClass : function(className){
7326             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7327         },
7328
7329         /**
7330          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7331          * @param {String} oldClassName The CSS class to replace
7332          * @param {String} newClassName The replacement CSS class
7333          * @return {Roo.Element} this
7334          */
7335         replaceClass : function(oldClassName, newClassName){
7336             this.removeClass(oldClassName);
7337             this.addClass(newClassName);
7338             return this;
7339         },
7340
7341         /**
7342          * Returns an object with properties matching the styles requested.
7343          * For example, el.getStyles('color', 'font-size', 'width') might return
7344          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7345          * @param {String} style1 A style name
7346          * @param {String} style2 A style name
7347          * @param {String} etc.
7348          * @return {Object} The style object
7349          */
7350         getStyles : function(){
7351             var a = arguments, len = a.length, r = {};
7352             for(var i = 0; i < len; i++){
7353                 r[a[i]] = this.getStyle(a[i]);
7354             }
7355             return r;
7356         },
7357
7358         /**
7359          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7360          * @param {String} property The style property whose value is returned.
7361          * @return {String} The current value of the style property for this element.
7362          */
7363         getStyle : function(){
7364             return view && view.getComputedStyle ?
7365                 function(prop){
7366                     var el = this.dom, v, cs, camel;
7367                     if(prop == 'float'){
7368                         prop = "cssFloat";
7369                     }
7370                     if(el.style && (v = el.style[prop])){
7371                         return v;
7372                     }
7373                     if(cs = view.getComputedStyle(el, "")){
7374                         if(!(camel = propCache[prop])){
7375                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7376                         }
7377                         return cs[camel];
7378                     }
7379                     return null;
7380                 } :
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'opacity'){
7384                         if(typeof el.style.filter == 'string'){
7385                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7386                             if(m){
7387                                 var fv = parseFloat(m[1]);
7388                                 if(!isNaN(fv)){
7389                                     return fv ? fv / 100 : 0;
7390                                 }
7391                             }
7392                         }
7393                         return 1;
7394                     }else if(prop == 'float'){
7395                         prop = "styleFloat";
7396                     }
7397                     if(!(camel = propCache[prop])){
7398                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7399                     }
7400                     if(v = el.style[camel]){
7401                         return v;
7402                     }
7403                     if(cs = el.currentStyle){
7404                         return cs[camel];
7405                     }
7406                     return null;
7407                 };
7408         }(),
7409
7410         /**
7411          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7412          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7413          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7414          * @return {Roo.Element} this
7415          */
7416         setStyle : function(prop, value){
7417             if(typeof prop == "string"){
7418                 
7419                 if (prop == 'float') {
7420                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7421                     return this;
7422                 }
7423                 
7424                 var camel;
7425                 if(!(camel = propCache[prop])){
7426                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7427                 }
7428                 
7429                 if(camel == 'opacity') {
7430                     this.setOpacity(value);
7431                 }else{
7432                     this.dom.style[camel] = value;
7433                 }
7434             }else{
7435                 for(var style in prop){
7436                     if(typeof prop[style] != "function"){
7437                        this.setStyle(style, prop[style]);
7438                     }
7439                 }
7440             }
7441             return this;
7442         },
7443
7444         /**
7445          * More flexible version of {@link #setStyle} for setting style properties.
7446          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7447          * a function which returns such a specification.
7448          * @return {Roo.Element} this
7449          */
7450         applyStyles : function(style){
7451             Roo.DomHelper.applyStyles(this.dom, style);
7452             return this;
7453         },
7454
7455         /**
7456           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7457           * @return {Number} The X position of the element
7458           */
7459         getX : function(){
7460             return D.getX(this.dom);
7461         },
7462
7463         /**
7464           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7465           * @return {Number} The Y position of the element
7466           */
7467         getY : function(){
7468             return D.getY(this.dom);
7469         },
7470
7471         /**
7472           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7473           * @return {Array} The XY position of the element
7474           */
7475         getXY : function(){
7476             return D.getXY(this.dom);
7477         },
7478
7479         /**
7480          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7481          * @param {Number} The X position of the element
7482          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7483          * @return {Roo.Element} this
7484          */
7485         setX : function(x, animate){
7486             if(!animate || !A){
7487                 D.setX(this.dom, x);
7488             }else{
7489                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7490             }
7491             return this;
7492         },
7493
7494         /**
7495          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7496          * @param {Number} The Y position of the element
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Roo.Element} this
7499          */
7500         setY : function(y, animate){
7501             if(!animate || !A){
7502                 D.setY(this.dom, y);
7503             }else{
7504                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7505             }
7506             return this;
7507         },
7508
7509         /**
7510          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7511          * @param {String} left The left CSS property value
7512          * @return {Roo.Element} this
7513          */
7514         setLeft : function(left){
7515             this.setStyle("left", this.addUnits(left));
7516             return this;
7517         },
7518
7519         /**
7520          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7521          * @param {String} top The top CSS property value
7522          * @return {Roo.Element} this
7523          */
7524         setTop : function(top){
7525             this.setStyle("top", this.addUnits(top));
7526             return this;
7527         },
7528
7529         /**
7530          * Sets the element's CSS right style.
7531          * @param {String} right The right CSS property value
7532          * @return {Roo.Element} this
7533          */
7534         setRight : function(right){
7535             this.setStyle("right", this.addUnits(right));
7536             return this;
7537         },
7538
7539         /**
7540          * Sets the element's CSS bottom style.
7541          * @param {String} bottom The bottom CSS property value
7542          * @return {Roo.Element} this
7543          */
7544         setBottom : function(bottom){
7545             this.setStyle("bottom", this.addUnits(bottom));
7546             return this;
7547         },
7548
7549         /**
7550          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7551          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7552          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7553          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7554          * @return {Roo.Element} this
7555          */
7556         setXY : function(pos, animate){
7557             if(!animate || !A){
7558                 D.setXY(this.dom, pos);
7559             }else{
7560                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7561             }
7562             return this;
7563         },
7564
7565         /**
7566          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7567          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7568          * @param {Number} x X value for new position (coordinates are page-based)
7569          * @param {Number} y Y value for new position (coordinates are page-based)
7570          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7571          * @return {Roo.Element} this
7572          */
7573         setLocation : function(x, y, animate){
7574             this.setXY([x, y], this.preanim(arguments, 2));
7575             return this;
7576         },
7577
7578         /**
7579          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7580          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7581          * @param {Number} x X value for new position (coordinates are page-based)
7582          * @param {Number} y Y value for new position (coordinates are page-based)
7583          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7584          * @return {Roo.Element} this
7585          */
7586         moveTo : function(x, y, animate){
7587             this.setXY([x, y], this.preanim(arguments, 2));
7588             return this;
7589         },
7590
7591         /**
7592          * Returns the region of the given element.
7593          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7594          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7595          */
7596         getRegion : function(){
7597             return D.getRegion(this.dom);
7598         },
7599
7600         /**
7601          * Returns the offset height of the element
7602          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7603          * @return {Number} The element's height
7604          */
7605         getHeight : function(contentHeight){
7606             var h = this.dom.offsetHeight || 0;
7607             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7608         },
7609
7610         /**
7611          * Returns the offset width of the element
7612          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7613          * @return {Number} The element's width
7614          */
7615         getWidth : function(contentWidth){
7616             var w = this.dom.offsetWidth || 0;
7617             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7618         },
7619
7620         /**
7621          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7622          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7623          * if a height has not been set using CSS.
7624          * @return {Number}
7625          */
7626         getComputedHeight : function(){
7627             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7628             if(!h){
7629                 h = parseInt(this.getStyle('height'), 10) || 0;
7630                 if(!this.isBorderBox()){
7631                     h += this.getFrameWidth('tb');
7632                 }
7633             }
7634             return h;
7635         },
7636
7637         /**
7638          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7639          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7640          * if a width has not been set using CSS.
7641          * @return {Number}
7642          */
7643         getComputedWidth : function(){
7644             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7645             if(!w){
7646                 w = parseInt(this.getStyle('width'), 10) || 0;
7647                 if(!this.isBorderBox()){
7648                     w += this.getFrameWidth('lr');
7649                 }
7650             }
7651             return w;
7652         },
7653
7654         /**
7655          * Returns the size of the element.
7656          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7657          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7658          */
7659         getSize : function(contentSize){
7660             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7661         },
7662
7663         /**
7664          * Returns the width and height of the viewport.
7665          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7666          */
7667         getViewSize : function(){
7668             var d = this.dom, doc = document, aw = 0, ah = 0;
7669             if(d == doc || d == doc.body){
7670                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7671             }else{
7672                 return {
7673                     width : d.clientWidth,
7674                     height: d.clientHeight
7675                 };
7676             }
7677         },
7678
7679         /**
7680          * Returns the value of the "value" attribute
7681          * @param {Boolean} asNumber true to parse the value as a number
7682          * @return {String/Number}
7683          */
7684         getValue : function(asNumber){
7685             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7686         },
7687
7688         // private
7689         adjustWidth : function(width){
7690             if(typeof width == "number"){
7691                 if(this.autoBoxAdjust && !this.isBorderBox()){
7692                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7693                 }
7694                 if(width < 0){
7695                     width = 0;
7696                 }
7697             }
7698             return width;
7699         },
7700
7701         // private
7702         adjustHeight : function(height){
7703             if(typeof height == "number"){
7704                if(this.autoBoxAdjust && !this.isBorderBox()){
7705                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7706                }
7707                if(height < 0){
7708                    height = 0;
7709                }
7710             }
7711             return height;
7712         },
7713
7714         /**
7715          * Set the width of the element
7716          * @param {Number} width The new width
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setWidth : function(width, animate){
7721             width = this.adjustWidth(width);
7722             if(!animate || !A){
7723                 this.dom.style.width = this.addUnits(width);
7724             }else{
7725                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7726             }
7727             return this;
7728         },
7729
7730         /**
7731          * Set the height of the element
7732          * @param {Number} height The new height
7733          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7734          * @return {Roo.Element} this
7735          */
7736          setHeight : function(height, animate){
7737             height = this.adjustHeight(height);
7738             if(!animate || !A){
7739                 this.dom.style.height = this.addUnits(height);
7740             }else{
7741                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7748          * @param {Number} width The new width
7749          * @param {Number} height The new height
7750          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7751          * @return {Roo.Element} this
7752          */
7753          setSize : function(width, height, animate){
7754             if(typeof width == "object"){ // in case of object from getSize()
7755                 height = width.height; width = width.width;
7756             }
7757             width = this.adjustWidth(width); height = this.adjustHeight(height);
7758             if(!animate || !A){
7759                 this.dom.style.width = this.addUnits(width);
7760                 this.dom.style.height = this.addUnits(height);
7761             }else{
7762                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7763             }
7764             return this;
7765         },
7766
7767         /**
7768          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7769          * @param {Number} x X value for new position (coordinates are page-based)
7770          * @param {Number} y Y value for new position (coordinates are page-based)
7771          * @param {Number} width The new width
7772          * @param {Number} height The new height
7773          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7774          * @return {Roo.Element} this
7775          */
7776         setBounds : function(x, y, width, height, animate){
7777             if(!animate || !A){
7778                 this.setSize(width, height);
7779                 this.setLocation(x, y);
7780             }else{
7781                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7782                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7783                               this.preanim(arguments, 4), 'motion');
7784             }
7785             return this;
7786         },
7787
7788         /**
7789          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7790          * @param {Roo.lib.Region} region The region to fill
7791          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794         setRegion : function(region, animate){
7795             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7796             return this;
7797         },
7798
7799         /**
7800          * Appends an event handler
7801          *
7802          * @param {String}   eventName     The type of event to append
7803          * @param {Function} fn        The method the event invokes
7804          * @param {Object} scope       (optional) The scope (this object) of the fn
7805          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7806          */
7807         addListener : function(eventName, fn, scope, options){
7808             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7809         },
7810
7811         /**
7812          * Removes an event handler from this element
7813          * @param {String} eventName the type of event to remove
7814          * @param {Function} fn the method the event invokes
7815          * @return {Roo.Element} this
7816          */
7817         removeListener : function(eventName, fn){
7818             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7819             return this;
7820         },
7821
7822         /**
7823          * Removes all previous added listeners from this element
7824          * @return {Roo.Element} this
7825          */
7826         removeAllListeners : function(){
7827             E.purgeElement(this.dom);
7828             return this;
7829         },
7830
7831         relayEvent : function(eventName, observable){
7832             this.on(eventName, function(e){
7833                 observable.fireEvent(eventName, e);
7834             });
7835         },
7836
7837         /**
7838          * Set the opacity of the element
7839          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7840          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843          setOpacity : function(opacity, animate){
7844             if(!animate || !A){
7845                 var s = this.dom.style;
7846                 if(Roo.isIE){
7847                     s.zoom = 1;
7848                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7849                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7850                 }else{
7851                     s.opacity = opacity;
7852                 }
7853             }else{
7854                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7855             }
7856             return this;
7857         },
7858
7859         /**
7860          * Gets the left X coordinate
7861          * @param {Boolean} local True to get the local css position instead of page coordinate
7862          * @return {Number}
7863          */
7864         getLeft : function(local){
7865             if(!local){
7866                 return this.getX();
7867             }else{
7868                 return parseInt(this.getStyle("left"), 10) || 0;
7869             }
7870         },
7871
7872         /**
7873          * Gets the right X coordinate of the element (element X position + element width)
7874          * @param {Boolean} local True to get the local css position instead of page coordinate
7875          * @return {Number}
7876          */
7877         getRight : function(local){
7878             if(!local){
7879                 return this.getX() + this.getWidth();
7880             }else{
7881                 return (this.getLeft(true) + this.getWidth()) || 0;
7882             }
7883         },
7884
7885         /**
7886          * Gets the top Y coordinate
7887          * @param {Boolean} local True to get the local css position instead of page coordinate
7888          * @return {Number}
7889          */
7890         getTop : function(local) {
7891             if(!local){
7892                 return this.getY();
7893             }else{
7894                 return parseInt(this.getStyle("top"), 10) || 0;
7895             }
7896         },
7897
7898         /**
7899          * Gets the bottom Y coordinate of the element (element Y position + element height)
7900          * @param {Boolean} local True to get the local css position instead of page coordinate
7901          * @return {Number}
7902          */
7903         getBottom : function(local){
7904             if(!local){
7905                 return this.getY() + this.getHeight();
7906             }else{
7907                 return (this.getTop(true) + this.getHeight()) || 0;
7908             }
7909         },
7910
7911         /**
7912         * Initializes positioning on this element. If a desired position is not passed, it will make the
7913         * the element positioned relative IF it is not already positioned.
7914         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7915         * @param {Number} zIndex (optional) The zIndex to apply
7916         * @param {Number} x (optional) Set the page X position
7917         * @param {Number} y (optional) Set the page Y position
7918         */
7919         position : function(pos, zIndex, x, y){
7920             if(!pos){
7921                if(this.getStyle('position') == 'static'){
7922                    this.setStyle('position', 'relative');
7923                }
7924             }else{
7925                 this.setStyle("position", pos);
7926             }
7927             if(zIndex){
7928                 this.setStyle("z-index", zIndex);
7929             }
7930             if(x !== undefined && y !== undefined){
7931                 this.setXY([x, y]);
7932             }else if(x !== undefined){
7933                 this.setX(x);
7934             }else if(y !== undefined){
7935                 this.setY(y);
7936             }
7937         },
7938
7939         /**
7940         * Clear positioning back to the default when the document was loaded
7941         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7942         * @return {Roo.Element} this
7943          */
7944         clearPositioning : function(value){
7945             value = value ||'';
7946             this.setStyle({
7947                 "left": value,
7948                 "right": value,
7949                 "top": value,
7950                 "bottom": value,
7951                 "z-index": "",
7952                 "position" : "static"
7953             });
7954             return this;
7955         },
7956
7957         /**
7958         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7959         * snapshot before performing an update and then restoring the element.
7960         * @return {Object}
7961         */
7962         getPositioning : function(){
7963             var l = this.getStyle("left");
7964             var t = this.getStyle("top");
7965             return {
7966                 "position" : this.getStyle("position"),
7967                 "left" : l,
7968                 "right" : l ? "" : this.getStyle("right"),
7969                 "top" : t,
7970                 "bottom" : t ? "" : this.getStyle("bottom"),
7971                 "z-index" : this.getStyle("z-index")
7972             };
7973         },
7974
7975         /**
7976          * Gets the width of the border(s) for the specified side(s)
7977          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7978          * passing lr would get the border (l)eft width + the border (r)ight width.
7979          * @return {Number} The width of the sides passed added together
7980          */
7981         getBorderWidth : function(side){
7982             return this.addStyles(side, El.borders);
7983         },
7984
7985         /**
7986          * Gets the width of the padding(s) for the specified side(s)
7987          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7988          * passing lr would get the padding (l)eft + the padding (r)ight.
7989          * @return {Number} The padding of the sides passed added together
7990          */
7991         getPadding : function(side){
7992             return this.addStyles(side, El.paddings);
7993         },
7994
7995         /**
7996         * Set positioning with an object returned by getPositioning().
7997         * @param {Object} posCfg
7998         * @return {Roo.Element} this
7999          */
8000         setPositioning : function(pc){
8001             this.applyStyles(pc);
8002             if(pc.right == "auto"){
8003                 this.dom.style.right = "";
8004             }
8005             if(pc.bottom == "auto"){
8006                 this.dom.style.bottom = "";
8007             }
8008             return this;
8009         },
8010
8011         // private
8012         fixDisplay : function(){
8013             if(this.getStyle("display") == "none"){
8014                 this.setStyle("visibility", "hidden");
8015                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8016                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8017                     this.setStyle("display", "block");
8018                 }
8019             }
8020         },
8021
8022         /**
8023          * Quick set left and top adding default units
8024          * @param {String} left The left CSS property value
8025          * @param {String} top The top CSS property value
8026          * @return {Roo.Element} this
8027          */
8028          setLeftTop : function(left, top){
8029             this.dom.style.left = this.addUnits(left);
8030             this.dom.style.top = this.addUnits(top);
8031             return this;
8032         },
8033
8034         /**
8035          * Move this element relative to its current position.
8036          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8037          * @param {Number} distance How far to move the element in pixels
8038          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8039          * @return {Roo.Element} this
8040          */
8041          move : function(direction, distance, animate){
8042             var xy = this.getXY();
8043             direction = direction.toLowerCase();
8044             switch(direction){
8045                 case "l":
8046                 case "left":
8047                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8048                     break;
8049                case "r":
8050                case "right":
8051                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8052                     break;
8053                case "t":
8054                case "top":
8055                case "up":
8056                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8057                     break;
8058                case "b":
8059                case "bottom":
8060                case "down":
8061                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8062                     break;
8063             }
8064             return this;
8065         },
8066
8067         /**
8068          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8069          * @return {Roo.Element} this
8070          */
8071         clip : function(){
8072             if(!this.isClipped){
8073                this.isClipped = true;
8074                this.originalClip = {
8075                    "o": this.getStyle("overflow"),
8076                    "x": this.getStyle("overflow-x"),
8077                    "y": this.getStyle("overflow-y")
8078                };
8079                this.setStyle("overflow", "hidden");
8080                this.setStyle("overflow-x", "hidden");
8081                this.setStyle("overflow-y", "hidden");
8082             }
8083             return this;
8084         },
8085
8086         /**
8087          *  Return clipping (overflow) to original clipping before clip() was called
8088          * @return {Roo.Element} this
8089          */
8090         unclip : function(){
8091             if(this.isClipped){
8092                 this.isClipped = false;
8093                 var o = this.originalClip;
8094                 if(o.o){this.setStyle("overflow", o.o);}
8095                 if(o.x){this.setStyle("overflow-x", o.x);}
8096                 if(o.y){this.setStyle("overflow-y", o.y);}
8097             }
8098             return this;
8099         },
8100
8101
8102         /**
8103          * Gets the x,y coordinates specified by the anchor position on the element.
8104          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8105          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8106          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8107          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8108          * @return {Array} [x, y] An array containing the element's x and y coordinates
8109          */
8110         getAnchorXY : function(anchor, local, s){
8111             //Passing a different size is useful for pre-calculating anchors,
8112             //especially for anchored animations that change the el size.
8113
8114             var w, h, vp = false;
8115             if(!s){
8116                 var d = this.dom;
8117                 if(d == document.body || d == document){
8118                     vp = true;
8119                     w = D.getViewWidth(); h = D.getViewHeight();
8120                 }else{
8121                     w = this.getWidth(); h = this.getHeight();
8122                 }
8123             }else{
8124                 w = s.width;  h = s.height;
8125             }
8126             var x = 0, y = 0, r = Math.round;
8127             switch((anchor || "tl").toLowerCase()){
8128                 case "c":
8129                     x = r(w*.5);
8130                     y = r(h*.5);
8131                 break;
8132                 case "t":
8133                     x = r(w*.5);
8134                     y = 0;
8135                 break;
8136                 case "l":
8137                     x = 0;
8138                     y = r(h*.5);
8139                 break;
8140                 case "r":
8141                     x = w;
8142                     y = r(h*.5);
8143                 break;
8144                 case "b":
8145                     x = r(w*.5);
8146                     y = h;
8147                 break;
8148                 case "tl":
8149                     x = 0;
8150                     y = 0;
8151                 break;
8152                 case "bl":
8153                     x = 0;
8154                     y = h;
8155                 break;
8156                 case "br":
8157                     x = w;
8158                     y = h;
8159                 break;
8160                 case "tr":
8161                     x = w;
8162                     y = 0;
8163                 break;
8164             }
8165             if(local === true){
8166                 return [x, y];
8167             }
8168             if(vp){
8169                 var sc = this.getScroll();
8170                 return [x + sc.left, y + sc.top];
8171             }
8172             //Add the element's offset xy
8173             var o = this.getXY();
8174             return [x+o[0], y+o[1]];
8175         },
8176
8177         /**
8178          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8179          * supported position values.
8180          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8181          * @param {String} position The position to align to.
8182          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8183          * @return {Array} [x, y]
8184          */
8185         getAlignToXY : function(el, p, o){
8186             el = Roo.get(el);
8187             var d = this.dom;
8188             if(!el.dom){
8189                 throw "Element.alignTo with an element that doesn't exist";
8190             }
8191             var c = false; //constrain to viewport
8192             var p1 = "", p2 = "";
8193             o = o || [0,0];
8194
8195             if(!p){
8196                 p = "tl-bl";
8197             }else if(p == "?"){
8198                 p = "tl-bl?";
8199             }else if(p.indexOf("-") == -1){
8200                 p = "tl-" + p;
8201             }
8202             p = p.toLowerCase();
8203             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8204             if(!m){
8205                throw "Element.alignTo with an invalid alignment " + p;
8206             }
8207             p1 = m[1]; p2 = m[2]; c = !!m[3];
8208
8209             //Subtract the aligned el's internal xy from the target's offset xy
8210             //plus custom offset to get the aligned el's new offset xy
8211             var a1 = this.getAnchorXY(p1, true);
8212             var a2 = el.getAnchorXY(p2, false);
8213             var x = a2[0] - a1[0] + o[0];
8214             var y = a2[1] - a1[1] + o[1];
8215             if(c){
8216                 //constrain the aligned el to viewport if necessary
8217                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8218                 // 5px of margin for ie
8219                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8220
8221                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8222                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8223                 //otherwise swap the aligned el to the opposite border of the target.
8224                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8225                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8226                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8227                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8228
8229                var doc = document;
8230                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8231                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8232
8233                if((x+w) > dw + scrollX){
8234                     x = swapX ? r.left-w : dw+scrollX-w;
8235                 }
8236                if(x < scrollX){
8237                    x = swapX ? r.right : scrollX;
8238                }
8239                if((y+h) > dh + scrollY){
8240                     y = swapY ? r.top-h : dh+scrollY-h;
8241                 }
8242                if (y < scrollY){
8243                    y = swapY ? r.bottom : scrollY;
8244                }
8245             }
8246             return [x,y];
8247         },
8248
8249         // private
8250         getConstrainToXY : function(){
8251             var os = {top:0, left:0, bottom:0, right: 0};
8252
8253             return function(el, local, offsets, proposedXY){
8254                 el = Roo.get(el);
8255                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8256
8257                 var vw, vh, vx = 0, vy = 0;
8258                 if(el.dom == document.body || el.dom == document){
8259                     vw = Roo.lib.Dom.getViewWidth();
8260                     vh = Roo.lib.Dom.getViewHeight();
8261                 }else{
8262                     vw = el.dom.clientWidth;
8263                     vh = el.dom.clientHeight;
8264                     if(!local){
8265                         var vxy = el.getXY();
8266                         vx = vxy[0];
8267                         vy = vxy[1];
8268                     }
8269                 }
8270
8271                 var s = el.getScroll();
8272
8273                 vx += offsets.left + s.left;
8274                 vy += offsets.top + s.top;
8275
8276                 vw -= offsets.right;
8277                 vh -= offsets.bottom;
8278
8279                 var vr = vx+vw;
8280                 var vb = vy+vh;
8281
8282                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8283                 var x = xy[0], y = xy[1];
8284                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8285
8286                 // only move it if it needs it
8287                 var moved = false;
8288
8289                 // first validate right/bottom
8290                 if((x + w) > vr){
8291                     x = vr - w;
8292                     moved = true;
8293                 }
8294                 if((y + h) > vb){
8295                     y = vb - h;
8296                     moved = true;
8297                 }
8298                 // then make sure top/left isn't negative
8299                 if(x < vx){
8300                     x = vx;
8301                     moved = true;
8302                 }
8303                 if(y < vy){
8304                     y = vy;
8305                     moved = true;
8306                 }
8307                 return moved ? [x, y] : false;
8308             };
8309         }(),
8310
8311         // private
8312         adjustForConstraints : function(xy, parent, offsets){
8313             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8314         },
8315
8316         /**
8317          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8318          * document it aligns it to the viewport.
8319          * The position parameter is optional, and can be specified in any one of the following formats:
8320          * <ul>
8321          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8322          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8323          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8324          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8325          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8326          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8327          * </ul>
8328          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8329          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8330          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8331          * that specified in order to enforce the viewport constraints.
8332          * Following are all of the supported anchor positions:
8333     <pre>
8334     Value  Description
8335     -----  -----------------------------
8336     tl     The top left corner (default)
8337     t      The center of the top edge
8338     tr     The top right corner
8339     l      The center of the left edge
8340     c      In the center of the element
8341     r      The center of the right edge
8342     bl     The bottom left corner
8343     b      The center of the bottom edge
8344     br     The bottom right corner
8345     </pre>
8346     Example Usage:
8347     <pre><code>
8348     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8349     el.alignTo("other-el");
8350
8351     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8352     el.alignTo("other-el", "tr?");
8353
8354     // align the bottom right corner of el with the center left edge of other-el
8355     el.alignTo("other-el", "br-l?");
8356
8357     // align the center of el with the bottom left corner of other-el and
8358     // adjust the x position by -6 pixels (and the y position by 0)
8359     el.alignTo("other-el", "c-bl", [-6, 0]);
8360     </code></pre>
8361          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8362          * @param {String} position The position to align to.
8363          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367         alignTo : function(element, position, offsets, animate){
8368             var xy = this.getAlignToXY(element, position, offsets);
8369             this.setXY(xy, this.preanim(arguments, 3));
8370             return this;
8371         },
8372
8373         /**
8374          * Anchors an element to another element and realigns it when the window is resized.
8375          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8376          * @param {String} position The position to align to.
8377          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8378          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8379          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8380          * is a number, it is used as the buffer delay (defaults to 50ms).
8381          * @param {Function} callback The function to call after the animation finishes
8382          * @return {Roo.Element} this
8383          */
8384         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8385             var action = function(){
8386                 this.alignTo(el, alignment, offsets, animate);
8387                 Roo.callback(callback, this);
8388             };
8389             Roo.EventManager.onWindowResize(action, this);
8390             var tm = typeof monitorScroll;
8391             if(tm != 'undefined'){
8392                 Roo.EventManager.on(window, 'scroll', action, this,
8393                     {buffer: tm == 'number' ? monitorScroll : 50});
8394             }
8395             action.call(this); // align immediately
8396             return this;
8397         },
8398         /**
8399          * Clears any opacity settings from this element. Required in some cases for IE.
8400          * @return {Roo.Element} this
8401          */
8402         clearOpacity : function(){
8403             if (window.ActiveXObject) {
8404                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8405                     this.dom.style.filter = "";
8406                 }
8407             } else {
8408                 this.dom.style.opacity = "";
8409                 this.dom.style["-moz-opacity"] = "";
8410                 this.dom.style["-khtml-opacity"] = "";
8411             }
8412             return this;
8413         },
8414
8415         /**
8416          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8417          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8418          * @return {Roo.Element} this
8419          */
8420         hide : function(animate){
8421             this.setVisible(false, this.preanim(arguments, 0));
8422             return this;
8423         },
8424
8425         /**
8426         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8427         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8428          * @return {Roo.Element} this
8429          */
8430         show : function(animate){
8431             this.setVisible(true, this.preanim(arguments, 0));
8432             return this;
8433         },
8434
8435         /**
8436          * @private Test if size has a unit, otherwise appends the default
8437          */
8438         addUnits : function(size){
8439             return Roo.Element.addUnits(size, this.defaultUnit);
8440         },
8441
8442         /**
8443          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8444          * @return {Roo.Element} this
8445          */
8446         beginMeasure : function(){
8447             var el = this.dom;
8448             if(el.offsetWidth || el.offsetHeight){
8449                 return this; // offsets work already
8450             }
8451             var changed = [];
8452             var p = this.dom, b = document.body; // start with this element
8453             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8454                 var pe = Roo.get(p);
8455                 if(pe.getStyle('display') == 'none'){
8456                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8457                     p.style.visibility = "hidden";
8458                     p.style.display = "block";
8459                 }
8460                 p = p.parentNode;
8461             }
8462             this._measureChanged = changed;
8463             return this;
8464
8465         },
8466
8467         /**
8468          * Restores displays to before beginMeasure was called
8469          * @return {Roo.Element} this
8470          */
8471         endMeasure : function(){
8472             var changed = this._measureChanged;
8473             if(changed){
8474                 for(var i = 0, len = changed.length; i < len; i++) {
8475                     var r = changed[i];
8476                     r.el.style.visibility = r.visibility;
8477                     r.el.style.display = "none";
8478                 }
8479                 this._measureChanged = null;
8480             }
8481             return this;
8482         },
8483
8484         /**
8485         * Update the innerHTML of this element, optionally searching for and processing scripts
8486         * @param {String} html The new HTML
8487         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8488         * @param {Function} callback For async script loading you can be noticed when the update completes
8489         * @return {Roo.Element} this
8490          */
8491         update : function(html, loadScripts, callback){
8492             if(typeof html == "undefined"){
8493                 html = "";
8494             }
8495             if(loadScripts !== true){
8496                 this.dom.innerHTML = html;
8497                 if(typeof callback == "function"){
8498                     callback();
8499                 }
8500                 return this;
8501             }
8502             var id = Roo.id();
8503             var dom = this.dom;
8504
8505             html += '<span id="' + id + '"></span>';
8506
8507             E.onAvailable(id, function(){
8508                 var hd = document.getElementsByTagName("head")[0];
8509                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8510                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8511                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8512
8513                 var match;
8514                 while(match = re.exec(html)){
8515                     var attrs = match[1];
8516                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8517                     if(srcMatch && srcMatch[2]){
8518                        var s = document.createElement("script");
8519                        s.src = srcMatch[2];
8520                        var typeMatch = attrs.match(typeRe);
8521                        if(typeMatch && typeMatch[2]){
8522                            s.type = typeMatch[2];
8523                        }
8524                        hd.appendChild(s);
8525                     }else if(match[2] && match[2].length > 0){
8526                         if(window.execScript) {
8527                            window.execScript(match[2]);
8528                         } else {
8529                             /**
8530                              * eval:var:id
8531                              * eval:var:dom
8532                              * eval:var:html
8533                              * 
8534                              */
8535                            window.eval(match[2]);
8536                         }
8537                     }
8538                 }
8539                 var el = document.getElementById(id);
8540                 if(el){el.parentNode.removeChild(el);}
8541                 if(typeof callback == "function"){
8542                     callback();
8543                 }
8544             });
8545             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8546             return this;
8547         },
8548
8549         /**
8550          * Direct access to the UpdateManager update() method (takes the same parameters).
8551          * @param {String/Function} url The url for this request or a function to call to get the url
8552          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8553          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8554          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8555          * @return {Roo.Element} this
8556          */
8557         load : function(){
8558             var um = this.getUpdateManager();
8559             um.update.apply(um, arguments);
8560             return this;
8561         },
8562
8563         /**
8564         * Gets this element's UpdateManager
8565         * @return {Roo.UpdateManager} The UpdateManager
8566         */
8567         getUpdateManager : function(){
8568             if(!this.updateManager){
8569                 this.updateManager = new Roo.UpdateManager(this);
8570             }
8571             return this.updateManager;
8572         },
8573
8574         /**
8575          * Disables text selection for this element (normalized across browsers)
8576          * @return {Roo.Element} this
8577          */
8578         unselectable : function(){
8579             this.dom.unselectable = "on";
8580             this.swallowEvent("selectstart", true);
8581             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8582             this.addClass("x-unselectable");
8583             return this;
8584         },
8585
8586         /**
8587         * Calculates the x, y to center this element on the screen
8588         * @return {Array} The x, y values [x, y]
8589         */
8590         getCenterXY : function(){
8591             return this.getAlignToXY(document, 'c-c');
8592         },
8593
8594         /**
8595         * Centers the Element in either the viewport, or another Element.
8596         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8597         */
8598         center : function(centerIn){
8599             this.alignTo(centerIn || document, 'c-c');
8600             return this;
8601         },
8602
8603         /**
8604          * Tests various css rules/browsers to determine if this element uses a border box
8605          * @return {Boolean}
8606          */
8607         isBorderBox : function(){
8608             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8609         },
8610
8611         /**
8612          * Return a box {x, y, width, height} that can be used to set another elements
8613          * size/location to match this element.
8614          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8615          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8616          * @return {Object} box An object in the format {x, y, width, height}
8617          */
8618         getBox : function(contentBox, local){
8619             var xy;
8620             if(!local){
8621                 xy = this.getXY();
8622             }else{
8623                 var left = parseInt(this.getStyle("left"), 10) || 0;
8624                 var top = parseInt(this.getStyle("top"), 10) || 0;
8625                 xy = [left, top];
8626             }
8627             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8628             if(!contentBox){
8629                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8630             }else{
8631                 var l = this.getBorderWidth("l")+this.getPadding("l");
8632                 var r = this.getBorderWidth("r")+this.getPadding("r");
8633                 var t = this.getBorderWidth("t")+this.getPadding("t");
8634                 var b = this.getBorderWidth("b")+this.getPadding("b");
8635                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8636             }
8637             bx.right = bx.x + bx.width;
8638             bx.bottom = bx.y + bx.height;
8639             return bx;
8640         },
8641
8642         /**
8643          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8644          for more information about the sides.
8645          * @param {String} sides
8646          * @return {Number}
8647          */
8648         getFrameWidth : function(sides, onlyContentBox){
8649             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8650         },
8651
8652         /**
8653          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8654          * @param {Object} box The box to fill {x, y, width, height}
8655          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8656          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         setBox : function(box, adjust, animate){
8660             var w = box.width, h = box.height;
8661             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8662                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8663                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8664             }
8665             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8666             return this;
8667         },
8668
8669         /**
8670          * Forces the browser to repaint this element
8671          * @return {Roo.Element} this
8672          */
8673          repaint : function(){
8674             var dom = this.dom;
8675             this.addClass("x-repaint");
8676             setTimeout(function(){
8677                 Roo.get(dom).removeClass("x-repaint");
8678             }, 1);
8679             return this;
8680         },
8681
8682         /**
8683          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8684          * then it returns the calculated width of the sides (see getPadding)
8685          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8686          * @return {Object/Number}
8687          */
8688         getMargins : function(side){
8689             if(!side){
8690                 return {
8691                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8692                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8693                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8694                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8695                 };
8696             }else{
8697                 return this.addStyles(side, El.margins);
8698              }
8699         },
8700
8701         // private
8702         addStyles : function(sides, styles){
8703             var val = 0, v, w;
8704             for(var i = 0, len = sides.length; i < len; i++){
8705                 v = this.getStyle(styles[sides.charAt(i)]);
8706                 if(v){
8707                      w = parseInt(v, 10);
8708                      if(w){ val += w; }
8709                 }
8710             }
8711             return val;
8712         },
8713
8714         /**
8715          * Creates a proxy element of this element
8716          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8717          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8718          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8719          * @return {Roo.Element} The new proxy element
8720          */
8721         createProxy : function(config, renderTo, matchBox){
8722             if(renderTo){
8723                 renderTo = Roo.getDom(renderTo);
8724             }else{
8725                 renderTo = document.body;
8726             }
8727             config = typeof config == "object" ?
8728                 config : {tag : "div", cls: config};
8729             var proxy = Roo.DomHelper.append(renderTo, config, true);
8730             if(matchBox){
8731                proxy.setBox(this.getBox());
8732             }
8733             return proxy;
8734         },
8735
8736         /**
8737          * Puts a mask over this element to disable user interaction. Requires core.css.
8738          * This method can only be applied to elements which accept child nodes.
8739          * @param {String} msg (optional) A message to display in the mask
8740          * @param {String} msgCls (optional) A css class to apply to the msg element
8741          * @return {Element} The mask  element
8742          */
8743         mask : function(msg, msgCls){
8744             if(this.getStyle("position") == "static"){
8745                 this.setStyle("position", "relative");
8746             }
8747             if(!this._mask){
8748                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8749             }
8750             this.addClass("x-masked");
8751             this._mask.setDisplayed(true);
8752             if(typeof msg == 'string'){
8753                 if(!this._maskMsg){
8754                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8755                 }
8756                 var mm = this._maskMsg;
8757                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8758                 mm.dom.firstChild.innerHTML = msg;
8759                 mm.setDisplayed(true);
8760                 mm.center(this);
8761             }
8762             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8763                 this._mask.setHeight(this.getHeight());
8764             }
8765             return this._mask;
8766         },
8767
8768         /**
8769          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8770          * it is cached for reuse.
8771          */
8772         unmask : function(removeEl){
8773             if(this._mask){
8774                 if(removeEl === true){
8775                     this._mask.remove();
8776                     delete this._mask;
8777                     if(this._maskMsg){
8778                         this._maskMsg.remove();
8779                         delete this._maskMsg;
8780                     }
8781                 }else{
8782                     this._mask.setDisplayed(false);
8783                     if(this._maskMsg){
8784                         this._maskMsg.setDisplayed(false);
8785                     }
8786                 }
8787             }
8788             this.removeClass("x-masked");
8789         },
8790
8791         /**
8792          * Returns true if this element is masked
8793          * @return {Boolean}
8794          */
8795         isMasked : function(){
8796             return this._mask && this._mask.isVisible();
8797         },
8798
8799         /**
8800          * Creates an iframe shim for this element to keep selects and other windowed objects from
8801          * showing through.
8802          * @return {Roo.Element} The new shim element
8803          */
8804         createShim : function(){
8805             var el = document.createElement('iframe');
8806             el.frameBorder = 'no';
8807             el.className = 'roo-shim';
8808             if(Roo.isIE && Roo.isSecure){
8809                 el.src = Roo.SSL_SECURE_URL;
8810             }
8811             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8812             shim.autoBoxAdjust = false;
8813             return shim;
8814         },
8815
8816         /**
8817          * Removes this element from the DOM and deletes it from the cache
8818          */
8819         remove : function(){
8820             if(this.dom.parentNode){
8821                 this.dom.parentNode.removeChild(this.dom);
8822             }
8823             delete El.cache[this.dom.id];
8824         },
8825
8826         /**
8827          * Sets up event handlers to add and remove a css class when the mouse is over this element
8828          * @param {String} className
8829          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8830          * mouseout events for children elements
8831          * @return {Roo.Element} this
8832          */
8833         addClassOnOver : function(className, preventFlicker){
8834             this.on("mouseover", function(){
8835                 Roo.fly(this, '_internal').addClass(className);
8836             }, this.dom);
8837             var removeFn = function(e){
8838                 if(preventFlicker !== true || !e.within(this, true)){
8839                     Roo.fly(this, '_internal').removeClass(className);
8840                 }
8841             };
8842             this.on("mouseout", removeFn, this.dom);
8843             return this;
8844         },
8845
8846         /**
8847          * Sets up event handlers to add and remove a css class when this element has the focus
8848          * @param {String} className
8849          * @return {Roo.Element} this
8850          */
8851         addClassOnFocus : function(className){
8852             this.on("focus", function(){
8853                 Roo.fly(this, '_internal').addClass(className);
8854             }, this.dom);
8855             this.on("blur", function(){
8856                 Roo.fly(this, '_internal').removeClass(className);
8857             }, this.dom);
8858             return this;
8859         },
8860         /**
8861          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8862          * @param {String} className
8863          * @return {Roo.Element} this
8864          */
8865         addClassOnClick : function(className){
8866             var dom = this.dom;
8867             this.on("mousedown", function(){
8868                 Roo.fly(dom, '_internal').addClass(className);
8869                 var d = Roo.get(document);
8870                 var fn = function(){
8871                     Roo.fly(dom, '_internal').removeClass(className);
8872                     d.removeListener("mouseup", fn);
8873                 };
8874                 d.on("mouseup", fn);
8875             });
8876             return this;
8877         },
8878
8879         /**
8880          * Stops the specified event from bubbling and optionally prevents the default action
8881          * @param {String} eventName
8882          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8883          * @return {Roo.Element} this
8884          */
8885         swallowEvent : function(eventName, preventDefault){
8886             var fn = function(e){
8887                 e.stopPropagation();
8888                 if(preventDefault){
8889                     e.preventDefault();
8890                 }
8891             };
8892             if(eventName instanceof Array){
8893                 for(var i = 0, len = eventName.length; i < len; i++){
8894                      this.on(eventName[i], fn);
8895                 }
8896                 return this;
8897             }
8898             this.on(eventName, fn);
8899             return this;
8900         },
8901
8902         /**
8903          * @private
8904          */
8905       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8906
8907         /**
8908          * Sizes this element to its parent element's dimensions performing
8909          * neccessary box adjustments.
8910          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8911          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8912          * @return {Roo.Element} this
8913          */
8914         fitToParent : function(monitorResize, targetParent) {
8915           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8916           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8917           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8918             return;
8919           }
8920           var p = Roo.get(targetParent || this.dom.parentNode);
8921           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8922           if (monitorResize === true) {
8923             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8924             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8925           }
8926           return this;
8927         },
8928
8929         /**
8930          * Gets the next sibling, skipping text nodes
8931          * @return {HTMLElement} The next sibling or null
8932          */
8933         getNextSibling : function(){
8934             var n = this.dom.nextSibling;
8935             while(n && n.nodeType != 1){
8936                 n = n.nextSibling;
8937             }
8938             return n;
8939         },
8940
8941         /**
8942          * Gets the previous sibling, skipping text nodes
8943          * @return {HTMLElement} The previous sibling or null
8944          */
8945         getPrevSibling : function(){
8946             var n = this.dom.previousSibling;
8947             while(n && n.nodeType != 1){
8948                 n = n.previousSibling;
8949             }
8950             return n;
8951         },
8952
8953
8954         /**
8955          * Appends the passed element(s) to this element
8956          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8957          * @return {Roo.Element} this
8958          */
8959         appendChild: function(el){
8960             el = Roo.get(el);
8961             el.appendTo(this);
8962             return this;
8963         },
8964
8965         /**
8966          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8967          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8968          * automatically generated with the specified attributes.
8969          * @param {HTMLElement} insertBefore (optional) a child element of this element
8970          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8971          * @return {Roo.Element} The new child element
8972          */
8973         createChild: function(config, insertBefore, returnDom){
8974             config = config || {tag:'div'};
8975             if(insertBefore){
8976                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8977             }
8978             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8979         },
8980
8981         /**
8982          * Appends this element to the passed element
8983          * @param {String/HTMLElement/Element} el The new parent element
8984          * @return {Roo.Element} this
8985          */
8986         appendTo: function(el){
8987             el = Roo.getDom(el);
8988             el.appendChild(this.dom);
8989             return this;
8990         },
8991
8992         /**
8993          * Inserts this element before the passed element in the DOM
8994          * @param {String/HTMLElement/Element} el The element to insert before
8995          * @return {Roo.Element} this
8996          */
8997         insertBefore: function(el){
8998             el = Roo.getDom(el);
8999             el.parentNode.insertBefore(this.dom, el);
9000             return this;
9001         },
9002
9003         /**
9004          * Inserts this element after the passed element in the DOM
9005          * @param {String/HTMLElement/Element} el The element to insert after
9006          * @return {Roo.Element} this
9007          */
9008         insertAfter: function(el){
9009             el = Roo.getDom(el);
9010             el.parentNode.insertBefore(this.dom, el.nextSibling);
9011             return this;
9012         },
9013
9014         /**
9015          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9016          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9017          * @return {Roo.Element} The new child
9018          */
9019         insertFirst: function(el, returnDom){
9020             el = el || {};
9021             if(typeof el == 'object' && !el.nodeType){ // dh config
9022                 return this.createChild(el, this.dom.firstChild, returnDom);
9023             }else{
9024                 el = Roo.getDom(el);
9025                 this.dom.insertBefore(el, this.dom.firstChild);
9026                 return !returnDom ? Roo.get(el) : el;
9027             }
9028         },
9029
9030         /**
9031          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9032          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9033          * @param {String} where (optional) 'before' or 'after' defaults to before
9034          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9035          * @return {Roo.Element} the inserted Element
9036          */
9037         insertSibling: function(el, where, returnDom){
9038             where = where ? where.toLowerCase() : 'before';
9039             el = el || {};
9040             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9041
9042             if(typeof el == 'object' && !el.nodeType){ // dh config
9043                 if(where == 'after' && !this.dom.nextSibling){
9044                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9045                 }else{
9046                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9047                 }
9048
9049             }else{
9050                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9051                             where == 'before' ? this.dom : this.dom.nextSibling);
9052                 if(!returnDom){
9053                     rt = Roo.get(rt);
9054                 }
9055             }
9056             return rt;
9057         },
9058
9059         /**
9060          * Creates and wraps this element with another element
9061          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9062          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9063          * @return {HTMLElement/Element} The newly created wrapper element
9064          */
9065         wrap: function(config, returnDom){
9066             if(!config){
9067                 config = {tag: "div"};
9068             }
9069             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9070             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9071             return newEl;
9072         },
9073
9074         /**
9075          * Replaces the passed element with this element
9076          * @param {String/HTMLElement/Element} el The element to replace
9077          * @return {Roo.Element} this
9078          */
9079         replace: function(el){
9080             el = Roo.get(el);
9081             this.insertBefore(el);
9082             el.remove();
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts an html fragment into this element
9088          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9089          * @param {String} html The HTML fragment
9090          * @param {Boolean} returnEl True to return an Roo.Element
9091          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9092          */
9093         insertHtml : function(where, html, returnEl){
9094             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9095             return returnEl ? Roo.get(el) : el;
9096         },
9097
9098         /**
9099          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9100          * @param {Object} o The object with the attributes
9101          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9102          * @return {Roo.Element} this
9103          */
9104         set : function(o, useSet){
9105             var el = this.dom;
9106             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9107             for(var attr in o){
9108                 if(attr == "style" || typeof o[attr] == "function") continue;
9109                 if(attr=="cls"){
9110                     el.className = o["cls"];
9111                 }else{
9112                     if(useSet) el.setAttribute(attr, o[attr]);
9113                     else el[attr] = o[attr];
9114                 }
9115             }
9116             if(o.style){
9117                 Roo.DomHelper.applyStyles(el, o.style);
9118             }
9119             return this;
9120         },
9121
9122         /**
9123          * Convenience method for constructing a KeyMap
9124          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9125          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9126          * @param {Function} fn The function to call
9127          * @param {Object} scope (optional) The scope of the function
9128          * @return {Roo.KeyMap} The KeyMap created
9129          */
9130         addKeyListener : function(key, fn, scope){
9131             var config;
9132             if(typeof key != "object" || key instanceof Array){
9133                 config = {
9134                     key: key,
9135                     fn: fn,
9136                     scope: scope
9137                 };
9138             }else{
9139                 config = {
9140                     key : key.key,
9141                     shift : key.shift,
9142                     ctrl : key.ctrl,
9143                     alt : key.alt,
9144                     fn: fn,
9145                     scope: scope
9146                 };
9147             }
9148             return new Roo.KeyMap(this, config);
9149         },
9150
9151         /**
9152          * Creates a KeyMap for this element
9153          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9154          * @return {Roo.KeyMap} The KeyMap created
9155          */
9156         addKeyMap : function(config){
9157             return new Roo.KeyMap(this, config);
9158         },
9159
9160         /**
9161          * Returns true if this element is scrollable.
9162          * @return {Boolean}
9163          */
9164          isScrollable : function(){
9165             var dom = this.dom;
9166             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9167         },
9168
9169         /**
9170          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9171          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9172          * @param {Number} value The new scroll value
9173          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9174          * @return {Element} this
9175          */
9176
9177         scrollTo : function(side, value, animate){
9178             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9179             if(!animate || !A){
9180                 this.dom[prop] = value;
9181             }else{
9182                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9183                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9190          * within this element's scrollable range.
9191          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9192          * @param {Number} distance How far to scroll the element in pixels
9193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9194          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9195          * was scrolled as far as it could go.
9196          */
9197          scroll : function(direction, distance, animate){
9198              if(!this.isScrollable()){
9199                  return;
9200              }
9201              var el = this.dom;
9202              var l = el.scrollLeft, t = el.scrollTop;
9203              var w = el.scrollWidth, h = el.scrollHeight;
9204              var cw = el.clientWidth, ch = el.clientHeight;
9205              direction = direction.toLowerCase();
9206              var scrolled = false;
9207              var a = this.preanim(arguments, 2);
9208              switch(direction){
9209                  case "l":
9210                  case "left":
9211                      if(w - l > cw){
9212                          var v = Math.min(l + distance, w-cw);
9213                          this.scrollTo("left", v, a);
9214                          scrolled = true;
9215                      }
9216                      break;
9217                 case "r":
9218                 case "right":
9219                      if(l > 0){
9220                          var v = Math.max(l - distance, 0);
9221                          this.scrollTo("left", v, a);
9222                          scrolled = true;
9223                      }
9224                      break;
9225                 case "t":
9226                 case "top":
9227                 case "up":
9228                      if(t > 0){
9229                          var v = Math.max(t - distance, 0);
9230                          this.scrollTo("top", v, a);
9231                          scrolled = true;
9232                      }
9233                      break;
9234                 case "b":
9235                 case "bottom":
9236                 case "down":
9237                      if(h - t > ch){
9238                          var v = Math.min(t + distance, h-ch);
9239                          this.scrollTo("top", v, a);
9240                          scrolled = true;
9241                      }
9242                      break;
9243              }
9244              return scrolled;
9245         },
9246
9247         /**
9248          * Translates the passed page coordinates into left/top css values for this element
9249          * @param {Number/Array} x The page x or an array containing [x, y]
9250          * @param {Number} y The page y
9251          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9252          */
9253         translatePoints : function(x, y){
9254             if(typeof x == 'object' || x instanceof Array){
9255                 y = x[1]; x = x[0];
9256             }
9257             var p = this.getStyle('position');
9258             var o = this.getXY();
9259
9260             var l = parseInt(this.getStyle('left'), 10);
9261             var t = parseInt(this.getStyle('top'), 10);
9262
9263             if(isNaN(l)){
9264                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9265             }
9266             if(isNaN(t)){
9267                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9268             }
9269
9270             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9271         },
9272
9273         /**
9274          * Returns the current scroll position of the element.
9275          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9276          */
9277         getScroll : function(){
9278             var d = this.dom, doc = document;
9279             if(d == doc || d == doc.body){
9280                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9281                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9282                 return {left: l, top: t};
9283             }else{
9284                 return {left: d.scrollLeft, top: d.scrollTop};
9285             }
9286         },
9287
9288         /**
9289          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9290          * are convert to standard 6 digit hex color.
9291          * @param {String} attr The css attribute
9292          * @param {String} defaultValue The default value to use when a valid color isn't found
9293          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9294          * YUI color anims.
9295          */
9296         getColor : function(attr, defaultValue, prefix){
9297             var v = this.getStyle(attr);
9298             if(!v || v == "transparent" || v == "inherit") {
9299                 return defaultValue;
9300             }
9301             var color = typeof prefix == "undefined" ? "#" : prefix;
9302             if(v.substr(0, 4) == "rgb("){
9303                 var rvs = v.slice(4, v.length -1).split(",");
9304                 for(var i = 0; i < 3; i++){
9305                     var h = parseInt(rvs[i]).toString(16);
9306                     if(h < 16){
9307                         h = "0" + h;
9308                     }
9309                     color += h;
9310                 }
9311             } else {
9312                 if(v.substr(0, 1) == "#"){
9313                     if(v.length == 4) {
9314                         for(var i = 1; i < 4; i++){
9315                             var c = v.charAt(i);
9316                             color +=  c + c;
9317                         }
9318                     }else if(v.length == 7){
9319                         color += v.substr(1);
9320                     }
9321                 }
9322             }
9323             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9324         },
9325
9326         /**
9327          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9328          * gradient background, rounded corners and a 4-way shadow.
9329          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9330          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9331          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9332          * @return {Roo.Element} this
9333          */
9334         boxWrap : function(cls){
9335             cls = cls || 'x-box';
9336             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9337             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9338             return el;
9339         },
9340
9341         /**
9342          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9343          * @param {String} namespace The namespace in which to look for the attribute
9344          * @param {String} name The attribute name
9345          * @return {String} The attribute value
9346          */
9347         getAttributeNS : Roo.isIE ? function(ns, name){
9348             var d = this.dom;
9349             var type = typeof d[ns+":"+name];
9350             if(type != 'undefined' && type != 'unknown'){
9351                 return d[ns+":"+name];
9352             }
9353             return d[name];
9354         } : function(ns, name){
9355             var d = this.dom;
9356             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9357         }
9358     };
9359
9360     var ep = El.prototype;
9361
9362     /**
9363      * Appends an event handler (Shorthand for addListener)
9364      * @param {String}   eventName     The type of event to append
9365      * @param {Function} fn        The method the event invokes
9366      * @param {Object} scope       (optional) The scope (this object) of the fn
9367      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9368      * @method
9369      */
9370     ep.on = ep.addListener;
9371         // backwards compat
9372     ep.mon = ep.addListener;
9373
9374     /**
9375      * Removes an event handler from this element (shorthand for removeListener)
9376      * @param {String} eventName the type of event to remove
9377      * @param {Function} fn the method the event invokes
9378      * @return {Roo.Element} this
9379      * @method
9380      */
9381     ep.un = ep.removeListener;
9382
9383     /**
9384      * true to automatically adjust width and height settings for box-model issues (default to true)
9385      */
9386     ep.autoBoxAdjust = true;
9387
9388     // private
9389     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9390
9391     // private
9392     El.addUnits = function(v, defaultUnit){
9393         if(v === "" || v == "auto"){
9394             return v;
9395         }
9396         if(v === undefined){
9397             return '';
9398         }
9399         if(typeof v == "number" || !El.unitPattern.test(v)){
9400             return v + (defaultUnit || 'px');
9401         }
9402         return v;
9403     };
9404
9405     // special markup used throughout Roo when box wrapping elements
9406     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9407     /**
9408      * Visibility mode constant - Use visibility to hide element
9409      * @static
9410      * @type Number
9411      */
9412     El.VISIBILITY = 1;
9413     /**
9414      * Visibility mode constant - Use display to hide element
9415      * @static
9416      * @type Number
9417      */
9418     El.DISPLAY = 2;
9419
9420     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9421     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9422     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9423
9424
9425
9426     /**
9427      * @private
9428      */
9429     El.cache = {};
9430
9431     var docEl;
9432
9433     /**
9434      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9435      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9436      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9437      * @return {Element} The Element object
9438      * @static
9439      */
9440     El.get = function(el){
9441         var ex, elm, id;
9442         if(!el){ return null; }
9443         if(typeof el == "string"){ // element id
9444             if(!(elm = document.getElementById(el))){
9445                 return null;
9446             }
9447             if(ex = El.cache[el]){
9448                 ex.dom = elm;
9449             }else{
9450                 ex = El.cache[el] = new El(elm);
9451             }
9452             return ex;
9453         }else if(el.tagName){ // dom element
9454             if(!(id = el.id)){
9455                 id = Roo.id(el);
9456             }
9457             if(ex = El.cache[id]){
9458                 ex.dom = el;
9459             }else{
9460                 ex = El.cache[id] = new El(el);
9461             }
9462             return ex;
9463         }else if(el instanceof El){
9464             if(el != docEl){
9465                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9466                                                               // catch case where it hasn't been appended
9467                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9468             }
9469             return el;
9470         }else if(el.isComposite){
9471             return el;
9472         }else if(el instanceof Array){
9473             return El.select(el);
9474         }else if(el == document){
9475             // create a bogus element object representing the document object
9476             if(!docEl){
9477                 var f = function(){};
9478                 f.prototype = El.prototype;
9479                 docEl = new f();
9480                 docEl.dom = document;
9481             }
9482             return docEl;
9483         }
9484         return null;
9485     };
9486
9487     // private
9488     El.uncache = function(el){
9489         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9490             if(a[i]){
9491                 delete El.cache[a[i].id || a[i]];
9492             }
9493         }
9494     };
9495
9496     // private
9497     // Garbage collection - uncache elements/purge listeners on orphaned elements
9498     // so we don't hold a reference and cause the browser to retain them
9499     El.garbageCollect = function(){
9500         if(!Roo.enableGarbageCollector){
9501             clearInterval(El.collectorThread);
9502             return;
9503         }
9504         for(var eid in El.cache){
9505             var el = El.cache[eid], d = el.dom;
9506             // -------------------------------------------------------
9507             // Determining what is garbage:
9508             // -------------------------------------------------------
9509             // !d
9510             // dom node is null, definitely garbage
9511             // -------------------------------------------------------
9512             // !d.parentNode
9513             // no parentNode == direct orphan, definitely garbage
9514             // -------------------------------------------------------
9515             // !d.offsetParent && !document.getElementById(eid)
9516             // display none elements have no offsetParent so we will
9517             // also try to look it up by it's id. However, check
9518             // offsetParent first so we don't do unneeded lookups.
9519             // This enables collection of elements that are not orphans
9520             // directly, but somewhere up the line they have an orphan
9521             // parent.
9522             // -------------------------------------------------------
9523             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9524                 delete El.cache[eid];
9525                 if(d && Roo.enableListenerCollection){
9526                     E.purgeElement(d);
9527                 }
9528             }
9529         }
9530     }
9531     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9532
9533
9534     // dom is optional
9535     El.Flyweight = function(dom){
9536         this.dom = dom;
9537     };
9538     El.Flyweight.prototype = El.prototype;
9539
9540     El._flyweights = {};
9541     /**
9542      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9543      * the dom node can be overwritten by other code.
9544      * @param {String/HTMLElement} el The dom node or id
9545      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9546      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9547      * @static
9548      * @return {Element} The shared Element object
9549      */
9550     El.fly = function(el, named){
9551         named = named || '_global';
9552         el = Roo.getDom(el);
9553         if(!el){
9554             return null;
9555         }
9556         if(!El._flyweights[named]){
9557             El._flyweights[named] = new El.Flyweight();
9558         }
9559         El._flyweights[named].dom = el;
9560         return El._flyweights[named];
9561     };
9562
9563     /**
9564      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9565      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9566      * Shorthand of {@link Roo.Element#get}
9567      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9568      * @return {Element} The Element object
9569      * @member Roo
9570      * @method get
9571      */
9572     Roo.get = El.get;
9573     /**
9574      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9575      * the dom node can be overwritten by other code.
9576      * Shorthand of {@link Roo.Element#fly}
9577      * @param {String/HTMLElement} el The dom node or id
9578      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9579      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9580      * @static
9581      * @return {Element} The shared Element object
9582      * @member Roo
9583      * @method fly
9584      */
9585     Roo.fly = El.fly;
9586
9587     // speedy lookup for elements never to box adjust
9588     var noBoxAdjust = Roo.isStrict ? {
9589         select:1
9590     } : {
9591         input:1, select:1, textarea:1
9592     };
9593     if(Roo.isIE || Roo.isGecko){
9594         noBoxAdjust['button'] = 1;
9595     }
9596
9597
9598     Roo.EventManager.on(window, 'unload', function(){
9599         delete El.cache;
9600         delete El._flyweights;
9601     });
9602 })();
9603
9604
9605
9606
9607 if(Roo.DomQuery){
9608     Roo.Element.selectorFunction = Roo.DomQuery.select;
9609 }
9610
9611 Roo.Element.select = function(selector, unique, root){
9612     var els;
9613     if(typeof selector == "string"){
9614         els = Roo.Element.selectorFunction(selector, root);
9615     }else if(selector.length !== undefined){
9616         els = selector;
9617     }else{
9618         throw "Invalid selector";
9619     }
9620     if(unique === true){
9621         return new Roo.CompositeElement(els);
9622     }else{
9623         return new Roo.CompositeElementLite(els);
9624     }
9625 };
9626 /**
9627  * Selects elements based on the passed CSS selector to enable working on them as 1.
9628  * @param {String/Array} selector The CSS selector or an array of elements
9629  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9630  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9631  * @return {CompositeElementLite/CompositeElement}
9632  * @member Roo
9633  * @method select
9634  */
9635 Roo.select = Roo.Element.select;
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650 /*
9651  * Based on:
9652  * Ext JS Library 1.1.1
9653  * Copyright(c) 2006-2007, Ext JS, LLC.
9654  *
9655  * Originally Released Under LGPL - original licence link has changed is not relivant.
9656  *
9657  * Fork - LGPL
9658  * <script type="text/javascript">
9659  */
9660
9661
9662
9663 //Notifies Element that fx methods are available
9664 Roo.enableFx = true;
9665
9666 /**
9667  * @class Roo.Fx
9668  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9669  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9670  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9671  * Element effects to work.</p><br/>
9672  *
9673  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9674  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9675  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9676  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9677  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9678  * expected results and should be done with care.</p><br/>
9679  *
9680  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9681  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9682 <pre>
9683 Value  Description
9684 -----  -----------------------------
9685 tl     The top left corner
9686 t      The center of the top edge
9687 tr     The top right corner
9688 l      The center of the left edge
9689 r      The center of the right edge
9690 bl     The bottom left corner
9691 b      The center of the bottom edge
9692 br     The bottom right corner
9693 </pre>
9694  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9695  * below are common options that can be passed to any Fx method.</b>
9696  * @cfg {Function} callback A function called when the effect is finished
9697  * @cfg {Object} scope The scope of the effect function
9698  * @cfg {String} easing A valid Easing value for the effect
9699  * @cfg {String} afterCls A css class to apply after the effect
9700  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9701  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9702  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9703  * effects that end with the element being visually hidden, ignored otherwise)
9704  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9705  * a function which returns such a specification that will be applied to the Element after the effect finishes
9706  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9707  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9708  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9709  */
9710 Roo.Fx = {
9711         /**
9712          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9713          * origin for the slide effect.  This function automatically handles wrapping the element with
9714          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9715          * Usage:
9716          *<pre><code>
9717 // default: slide the element in from the top
9718 el.slideIn();
9719
9720 // custom: slide the element in from the right with a 2-second duration
9721 el.slideIn('r', { duration: 2 });
9722
9723 // common config options shown with default values
9724 el.slideIn('t', {
9725     easing: 'easeOut',
9726     duration: .5
9727 });
9728 </code></pre>
9729          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9730          * @param {Object} options (optional) Object literal with any of the Fx config options
9731          * @return {Roo.Element} The Element
9732          */
9733     slideIn : function(anchor, o){
9734         var el = this.getFxEl();
9735         o = o || {};
9736
9737         el.queueFx(o, function(){
9738
9739             anchor = anchor || "t";
9740
9741             // fix display to visibility
9742             this.fixDisplay();
9743
9744             // restore values after effect
9745             var r = this.getFxRestore();
9746             var b = this.getBox();
9747             // fixed size for slide
9748             this.setSize(b);
9749
9750             // wrap if needed
9751             var wrap = this.fxWrap(r.pos, o, "hidden");
9752
9753             var st = this.dom.style;
9754             st.visibility = "visible";
9755             st.position = "absolute";
9756
9757             // clear out temp styles after slide and unwrap
9758             var after = function(){
9759                 el.fxUnwrap(wrap, r.pos, o);
9760                 st.width = r.width;
9761                 st.height = r.height;
9762                 el.afterFx(o);
9763             };
9764             // time to calc the positions
9765             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9766
9767             switch(anchor.toLowerCase()){
9768                 case "t":
9769                     wrap.setSize(b.width, 0);
9770                     st.left = st.bottom = "0";
9771                     a = {height: bh};
9772                 break;
9773                 case "l":
9774                     wrap.setSize(0, b.height);
9775                     st.right = st.top = "0";
9776                     a = {width: bw};
9777                 break;
9778                 case "r":
9779                     wrap.setSize(0, b.height);
9780                     wrap.setX(b.right);
9781                     st.left = st.top = "0";
9782                     a = {width: bw, points: pt};
9783                 break;
9784                 case "b":
9785                     wrap.setSize(b.width, 0);
9786                     wrap.setY(b.bottom);
9787                     st.left = st.top = "0";
9788                     a = {height: bh, points: pt};
9789                 break;
9790                 case "tl":
9791                     wrap.setSize(0, 0);
9792                     st.right = st.bottom = "0";
9793                     a = {width: bw, height: bh};
9794                 break;
9795                 case "bl":
9796                     wrap.setSize(0, 0);
9797                     wrap.setY(b.y+b.height);
9798                     st.right = st.top = "0";
9799                     a = {width: bw, height: bh, points: pt};
9800                 break;
9801                 case "br":
9802                     wrap.setSize(0, 0);
9803                     wrap.setXY([b.right, b.bottom]);
9804                     st.left = st.top = "0";
9805                     a = {width: bw, height: bh, points: pt};
9806                 break;
9807                 case "tr":
9808                     wrap.setSize(0, 0);
9809                     wrap.setX(b.x+b.width);
9810                     st.left = st.bottom = "0";
9811                     a = {width: bw, height: bh, points: pt};
9812                 break;
9813             }
9814             this.dom.style.visibility = "visible";
9815             wrap.show();
9816
9817             arguments.callee.anim = wrap.fxanim(a,
9818                 o,
9819                 'motion',
9820                 .5,
9821                 'easeOut', after);
9822         });
9823         return this;
9824     },
9825     
9826         /**
9827          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9828          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9829          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9830          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9831          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9832          * Usage:
9833          *<pre><code>
9834 // default: slide the element out to the top
9835 el.slideOut();
9836
9837 // custom: slide the element out to the right with a 2-second duration
9838 el.slideOut('r', { duration: 2 });
9839
9840 // common config options shown with default values
9841 el.slideOut('t', {
9842     easing: 'easeOut',
9843     duration: .5,
9844     remove: false,
9845     useDisplay: false
9846 });
9847 </code></pre>
9848          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9849          * @param {Object} options (optional) Object literal with any of the Fx config options
9850          * @return {Roo.Element} The Element
9851          */
9852     slideOut : function(anchor, o){
9853         var el = this.getFxEl();
9854         o = o || {};
9855
9856         el.queueFx(o, function(){
9857
9858             anchor = anchor || "t";
9859
9860             // restore values after effect
9861             var r = this.getFxRestore();
9862             
9863             var b = this.getBox();
9864             // fixed size for slide
9865             this.setSize(b);
9866
9867             // wrap if needed
9868             var wrap = this.fxWrap(r.pos, o, "visible");
9869
9870             var st = this.dom.style;
9871             st.visibility = "visible";
9872             st.position = "absolute";
9873
9874             wrap.setSize(b);
9875
9876             var after = function(){
9877                 if(o.useDisplay){
9878                     el.setDisplayed(false);
9879                 }else{
9880                     el.hide();
9881                 }
9882
9883                 el.fxUnwrap(wrap, r.pos, o);
9884
9885                 st.width = r.width;
9886                 st.height = r.height;
9887
9888                 el.afterFx(o);
9889             };
9890
9891             var a, zero = {to: 0};
9892             switch(anchor.toLowerCase()){
9893                 case "t":
9894                     st.left = st.bottom = "0";
9895                     a = {height: zero};
9896                 break;
9897                 case "l":
9898                     st.right = st.top = "0";
9899                     a = {width: zero};
9900                 break;
9901                 case "r":
9902                     st.left = st.top = "0";
9903                     a = {width: zero, points: {to:[b.right, b.y]}};
9904                 break;
9905                 case "b":
9906                     st.left = st.top = "0";
9907                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9908                 break;
9909                 case "tl":
9910                     st.right = st.bottom = "0";
9911                     a = {width: zero, height: zero};
9912                 break;
9913                 case "bl":
9914                     st.right = st.top = "0";
9915                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9916                 break;
9917                 case "br":
9918                     st.left = st.top = "0";
9919                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9920                 break;
9921                 case "tr":
9922                     st.left = st.bottom = "0";
9923                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9924                 break;
9925             }
9926
9927             arguments.callee.anim = wrap.fxanim(a,
9928                 o,
9929                 'motion',
9930                 .5,
9931                 "easeOut", after);
9932         });
9933         return this;
9934     },
9935
9936         /**
9937          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9938          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9939          * The element must be removed from the DOM using the 'remove' config option if desired.
9940          * Usage:
9941          *<pre><code>
9942 // default
9943 el.puff();
9944
9945 // common config options shown with default values
9946 el.puff({
9947     easing: 'easeOut',
9948     duration: .5,
9949     remove: false,
9950     useDisplay: false
9951 });
9952 </code></pre>
9953          * @param {Object} options (optional) Object literal with any of the Fx config options
9954          * @return {Roo.Element} The Element
9955          */
9956     puff : function(o){
9957         var el = this.getFxEl();
9958         o = o || {};
9959
9960         el.queueFx(o, function(){
9961             this.clearOpacity();
9962             this.show();
9963
9964             // restore values after effect
9965             var r = this.getFxRestore();
9966             var st = this.dom.style;
9967
9968             var after = function(){
9969                 if(o.useDisplay){
9970                     el.setDisplayed(false);
9971                 }else{
9972                     el.hide();
9973                 }
9974
9975                 el.clearOpacity();
9976
9977                 el.setPositioning(r.pos);
9978                 st.width = r.width;
9979                 st.height = r.height;
9980                 st.fontSize = '';
9981                 el.afterFx(o);
9982             };
9983
9984             var width = this.getWidth();
9985             var height = this.getHeight();
9986
9987             arguments.callee.anim = this.fxanim({
9988                     width : {to: this.adjustWidth(width * 2)},
9989                     height : {to: this.adjustHeight(height * 2)},
9990                     points : {by: [-(width * .5), -(height * .5)]},
9991                     opacity : {to: 0},
9992                     fontSize: {to:200, unit: "%"}
9993                 },
9994                 o,
9995                 'motion',
9996                 .5,
9997                 "easeOut", after);
9998         });
9999         return this;
10000     },
10001
10002         /**
10003          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10004          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10005          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10006          * Usage:
10007          *<pre><code>
10008 // default
10009 el.switchOff();
10010
10011 // all config options shown with default values
10012 el.switchOff({
10013     easing: 'easeIn',
10014     duration: .3,
10015     remove: false,
10016     useDisplay: false
10017 });
10018 </code></pre>
10019          * @param {Object} options (optional) Object literal with any of the Fx config options
10020          * @return {Roo.Element} The Element
10021          */
10022     switchOff : function(o){
10023         var el = this.getFxEl();
10024         o = o || {};
10025
10026         el.queueFx(o, function(){
10027             this.clearOpacity();
10028             this.clip();
10029
10030             // restore values after effect
10031             var r = this.getFxRestore();
10032             var st = this.dom.style;
10033
10034             var after = function(){
10035                 if(o.useDisplay){
10036                     el.setDisplayed(false);
10037                 }else{
10038                     el.hide();
10039                 }
10040
10041                 el.clearOpacity();
10042                 el.setPositioning(r.pos);
10043                 st.width = r.width;
10044                 st.height = r.height;
10045
10046                 el.afterFx(o);
10047             };
10048
10049             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10050                 this.clearOpacity();
10051                 (function(){
10052                     this.fxanim({
10053                         height:{to:1},
10054                         points:{by:[0, this.getHeight() * .5]}
10055                     }, o, 'motion', 0.3, 'easeIn', after);
10056                 }).defer(100, this);
10057             });
10058         });
10059         return this;
10060     },
10061
10062     /**
10063      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10064      * changed using the "attr" config option) and then fading back to the original color. If no original
10065      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10066      * Usage:
10067 <pre><code>
10068 // default: highlight background to yellow
10069 el.highlight();
10070
10071 // custom: highlight foreground text to blue for 2 seconds
10072 el.highlight("0000ff", { attr: 'color', duration: 2 });
10073
10074 // common config options shown with default values
10075 el.highlight("ffff9c", {
10076     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10077     endColor: (current color) or "ffffff",
10078     easing: 'easeIn',
10079     duration: 1
10080 });
10081 </code></pre>
10082      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10083      * @param {Object} options (optional) Object literal with any of the Fx config options
10084      * @return {Roo.Element} The Element
10085      */ 
10086     highlight : function(color, o){
10087         var el = this.getFxEl();
10088         o = o || {};
10089
10090         el.queueFx(o, function(){
10091             color = color || "ffff9c";
10092             attr = o.attr || "backgroundColor";
10093
10094             this.clearOpacity();
10095             this.show();
10096
10097             var origColor = this.getColor(attr);
10098             var restoreColor = this.dom.style[attr];
10099             endColor = (o.endColor || origColor) || "ffffff";
10100
10101             var after = function(){
10102                 el.dom.style[attr] = restoreColor;
10103                 el.afterFx(o);
10104             };
10105
10106             var a = {};
10107             a[attr] = {from: color, to: endColor};
10108             arguments.callee.anim = this.fxanim(a,
10109                 o,
10110                 'color',
10111                 1,
10112                 'easeIn', after);
10113         });
10114         return this;
10115     },
10116
10117    /**
10118     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10119     * Usage:
10120 <pre><code>
10121 // default: a single light blue ripple
10122 el.frame();
10123
10124 // custom: 3 red ripples lasting 3 seconds total
10125 el.frame("ff0000", 3, { duration: 3 });
10126
10127 // common config options shown with default values
10128 el.frame("C3DAF9", 1, {
10129     duration: 1 //duration of entire animation (not each individual ripple)
10130     // Note: Easing is not configurable and will be ignored if included
10131 });
10132 </code></pre>
10133     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10134     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10135     * @param {Object} options (optional) Object literal with any of the Fx config options
10136     * @return {Roo.Element} The Element
10137     */
10138     frame : function(color, count, o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             color = color || "#C3DAF9";
10144             if(color.length == 6){
10145                 color = "#" + color;
10146             }
10147             count = count || 1;
10148             duration = o.duration || 1;
10149             this.show();
10150
10151             var b = this.getBox();
10152             var animFn = function(){
10153                 var proxy = this.createProxy({
10154
10155                      style:{
10156                         visbility:"hidden",
10157                         position:"absolute",
10158                         "z-index":"35000", // yee haw
10159                         border:"0px solid " + color
10160                      }
10161                   });
10162                 var scale = Roo.isBorderBox ? 2 : 1;
10163                 proxy.animate({
10164                     top:{from:b.y, to:b.y - 20},
10165                     left:{from:b.x, to:b.x - 20},
10166                     borderWidth:{from:0, to:10},
10167                     opacity:{from:1, to:0},
10168                     height:{from:b.height, to:(b.height + (20*scale))},
10169                     width:{from:b.width, to:(b.width + (20*scale))}
10170                 }, duration, function(){
10171                     proxy.remove();
10172                 });
10173                 if(--count > 0){
10174                      animFn.defer((duration/2)*1000, this);
10175                 }else{
10176                     el.afterFx(o);
10177                 }
10178             };
10179             animFn.call(this);
10180         });
10181         return this;
10182     },
10183
10184    /**
10185     * Creates a pause before any subsequent queued effects begin.  If there are
10186     * no effects queued after the pause it will have no effect.
10187     * Usage:
10188 <pre><code>
10189 el.pause(1);
10190 </code></pre>
10191     * @param {Number} seconds The length of time to pause (in seconds)
10192     * @return {Roo.Element} The Element
10193     */
10194     pause : function(seconds){
10195         var el = this.getFxEl();
10196         var o = {};
10197
10198         el.queueFx(o, function(){
10199             setTimeout(function(){
10200                 el.afterFx(o);
10201             }, seconds * 1000);
10202         });
10203         return this;
10204     },
10205
10206    /**
10207     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10208     * using the "endOpacity" config option.
10209     * Usage:
10210 <pre><code>
10211 // default: fade in from opacity 0 to 100%
10212 el.fadeIn();
10213
10214 // custom: fade in from opacity 0 to 75% over 2 seconds
10215 el.fadeIn({ endOpacity: .75, duration: 2});
10216
10217 // common config options shown with default values
10218 el.fadeIn({
10219     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10220     easing: 'easeOut',
10221     duration: .5
10222 });
10223 </code></pre>
10224     * @param {Object} options (optional) Object literal with any of the Fx config options
10225     * @return {Roo.Element} The Element
10226     */
10227     fadeIn : function(o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230         el.queueFx(o, function(){
10231             this.setOpacity(0);
10232             this.fixDisplay();
10233             this.dom.style.visibility = 'visible';
10234             var to = o.endOpacity || 1;
10235             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10236                 o, null, .5, "easeOut", function(){
10237                 if(to == 1){
10238                     this.clearOpacity();
10239                 }
10240                 el.afterFx(o);
10241             });
10242         });
10243         return this;
10244     },
10245
10246    /**
10247     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10248     * using the "endOpacity" config option.
10249     * Usage:
10250 <pre><code>
10251 // default: fade out from the element's current opacity to 0
10252 el.fadeOut();
10253
10254 // custom: fade out from the element's current opacity to 25% over 2 seconds
10255 el.fadeOut({ endOpacity: .25, duration: 2});
10256
10257 // common config options shown with default values
10258 el.fadeOut({
10259     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10260     easing: 'easeOut',
10261     duration: .5
10262     remove: false,
10263     useDisplay: false
10264 });
10265 </code></pre>
10266     * @param {Object} options (optional) Object literal with any of the Fx config options
10267     * @return {Roo.Element} The Element
10268     */
10269     fadeOut : function(o){
10270         var el = this.getFxEl();
10271         o = o || {};
10272         el.queueFx(o, function(){
10273             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10274                 o, null, .5, "easeOut", function(){
10275                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10276                      this.dom.style.display = "none";
10277                 }else{
10278                      this.dom.style.visibility = "hidden";
10279                 }
10280                 this.clearOpacity();
10281                 el.afterFx(o);
10282             });
10283         });
10284         return this;
10285     },
10286
10287    /**
10288     * Animates the transition of an element's dimensions from a starting height/width
10289     * to an ending height/width.
10290     * Usage:
10291 <pre><code>
10292 // change height and width to 100x100 pixels
10293 el.scale(100, 100);
10294
10295 // common config options shown with default values.  The height and width will default to
10296 // the element's existing values if passed as null.
10297 el.scale(
10298     [element's width],
10299     [element's height], {
10300     easing: 'easeOut',
10301     duration: .35
10302 });
10303 </code></pre>
10304     * @param {Number} width  The new width (pass undefined to keep the original width)
10305     * @param {Number} height  The new height (pass undefined to keep the original height)
10306     * @param {Object} options (optional) Object literal with any of the Fx config options
10307     * @return {Roo.Element} The Element
10308     */
10309     scale : function(w, h, o){
10310         this.shift(Roo.apply({}, o, {
10311             width: w,
10312             height: h
10313         }));
10314         return this;
10315     },
10316
10317    /**
10318     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10319     * Any of these properties not specified in the config object will not be changed.  This effect 
10320     * requires that at least one new dimension, position or opacity setting must be passed in on
10321     * the config object in order for the function to have any effect.
10322     * Usage:
10323 <pre><code>
10324 // slide the element horizontally to x position 200 while changing the height and opacity
10325 el.shift({ x: 200, height: 50, opacity: .8 });
10326
10327 // common config options shown with default values.
10328 el.shift({
10329     width: [element's width],
10330     height: [element's height],
10331     x: [element's x position],
10332     y: [element's y position],
10333     opacity: [element's opacity],
10334     easing: 'easeOut',
10335     duration: .35
10336 });
10337 </code></pre>
10338     * @param {Object} options  Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     shift : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10346             if(w !== undefined){
10347                 a.width = {to: this.adjustWidth(w)};
10348             }
10349             if(h !== undefined){
10350                 a.height = {to: this.adjustHeight(h)};
10351             }
10352             if(x !== undefined || y !== undefined){
10353                 a.points = {to: [
10354                     x !== undefined ? x : this.getX(),
10355                     y !== undefined ? y : this.getY()
10356                 ]};
10357             }
10358             if(op !== undefined){
10359                 a.opacity = {to: op};
10360             }
10361             if(o.xy !== undefined){
10362                 a.points = {to: o.xy};
10363             }
10364             arguments.callee.anim = this.fxanim(a,
10365                 o, 'motion', .35, "easeOut", function(){
10366                 el.afterFx(o);
10367             });
10368         });
10369         return this;
10370     },
10371
10372         /**
10373          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10374          * ending point of the effect.
10375          * Usage:
10376          *<pre><code>
10377 // default: slide the element downward while fading out
10378 el.ghost();
10379
10380 // custom: slide the element out to the right with a 2-second duration
10381 el.ghost('r', { duration: 2 });
10382
10383 // common config options shown with default values
10384 el.ghost('b', {
10385     easing: 'easeOut',
10386     duration: .5
10387     remove: false,
10388     useDisplay: false
10389 });
10390 </code></pre>
10391          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10392          * @param {Object} options (optional) Object literal with any of the Fx config options
10393          * @return {Roo.Element} The Element
10394          */
10395     ghost : function(anchor, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             anchor = anchor || "b";
10401
10402             // restore values after effect
10403             var r = this.getFxRestore();
10404             var w = this.getWidth(),
10405                 h = this.getHeight();
10406
10407             var st = this.dom.style;
10408
10409             var after = function(){
10410                 if(o.useDisplay){
10411                     el.setDisplayed(false);
10412                 }else{
10413                     el.hide();
10414                 }
10415
10416                 el.clearOpacity();
10417                 el.setPositioning(r.pos);
10418                 st.width = r.width;
10419                 st.height = r.height;
10420
10421                 el.afterFx(o);
10422             };
10423
10424             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10425             switch(anchor.toLowerCase()){
10426                 case "t":
10427                     pt.by = [0, -h];
10428                 break;
10429                 case "l":
10430                     pt.by = [-w, 0];
10431                 break;
10432                 case "r":
10433                     pt.by = [w, 0];
10434                 break;
10435                 case "b":
10436                     pt.by = [0, h];
10437                 break;
10438                 case "tl":
10439                     pt.by = [-w, -h];
10440                 break;
10441                 case "bl":
10442                     pt.by = [-w, h];
10443                 break;
10444                 case "br":
10445                     pt.by = [w, h];
10446                 break;
10447                 case "tr":
10448                     pt.by = [w, -h];
10449                 break;
10450             }
10451
10452             arguments.callee.anim = this.fxanim(a,
10453                 o,
10454                 'motion',
10455                 .5,
10456                 "easeOut", after);
10457         });
10458         return this;
10459     },
10460
10461         /**
10462          * Ensures that all effects queued after syncFx is called on the element are
10463          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10464          * @return {Roo.Element} The Element
10465          */
10466     syncFx : function(){
10467         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10468             block : false,
10469             concurrent : true,
10470             stopFx : false
10471         });
10472         return this;
10473     },
10474
10475         /**
10476          * Ensures that all effects queued after sequenceFx is called on the element are
10477          * run in sequence.  This is the opposite of {@link #syncFx}.
10478          * @return {Roo.Element} The Element
10479          */
10480     sequenceFx : function(){
10481         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10482             block : false,
10483             concurrent : false,
10484             stopFx : false
10485         });
10486         return this;
10487     },
10488
10489         /* @private */
10490     nextFx : function(){
10491         var ef = this.fxQueue[0];
10492         if(ef){
10493             ef.call(this);
10494         }
10495     },
10496
10497         /**
10498          * Returns true if the element has any effects actively running or queued, else returns false.
10499          * @return {Boolean} True if element has active effects, else false
10500          */
10501     hasActiveFx : function(){
10502         return this.fxQueue && this.fxQueue[0];
10503     },
10504
10505         /**
10506          * Stops any running effects and clears the element's internal effects queue if it contains
10507          * any additional effects that haven't started yet.
10508          * @return {Roo.Element} The Element
10509          */
10510     stopFx : function(){
10511         if(this.hasActiveFx()){
10512             var cur = this.fxQueue[0];
10513             if(cur && cur.anim && cur.anim.isAnimated()){
10514                 this.fxQueue = [cur]; // clear out others
10515                 cur.anim.stop(true);
10516             }
10517         }
10518         return this;
10519     },
10520
10521         /* @private */
10522     beforeFx : function(o){
10523         if(this.hasActiveFx() && !o.concurrent){
10524            if(o.stopFx){
10525                this.stopFx();
10526                return true;
10527            }
10528            return false;
10529         }
10530         return true;
10531     },
10532
10533         /**
10534          * Returns true if the element is currently blocking so that no other effect can be queued
10535          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10536          * used to ensure that an effect initiated by a user action runs to completion prior to the
10537          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10538          * @return {Boolean} True if blocking, else false
10539          */
10540     hasFxBlock : function(){
10541         var q = this.fxQueue;
10542         return q && q[0] && q[0].block;
10543     },
10544
10545         /* @private */
10546     queueFx : function(o, fn){
10547         if(!this.fxQueue){
10548             this.fxQueue = [];
10549         }
10550         if(!this.hasFxBlock()){
10551             Roo.applyIf(o, this.fxDefaults);
10552             if(!o.concurrent){
10553                 var run = this.beforeFx(o);
10554                 fn.block = o.block;
10555                 this.fxQueue.push(fn);
10556                 if(run){
10557                     this.nextFx();
10558                 }
10559             }else{
10560                 fn.call(this);
10561             }
10562         }
10563         return this;
10564     },
10565
10566         /* @private */
10567     fxWrap : function(pos, o, vis){
10568         var wrap;
10569         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10570             var wrapXY;
10571             if(o.fixPosition){
10572                 wrapXY = this.getXY();
10573             }
10574             var div = document.createElement("div");
10575             div.style.visibility = vis;
10576             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10577             wrap.setPositioning(pos);
10578             if(wrap.getStyle("position") == "static"){
10579                 wrap.position("relative");
10580             }
10581             this.clearPositioning('auto');
10582             wrap.clip();
10583             wrap.dom.appendChild(this.dom);
10584             if(wrapXY){
10585                 wrap.setXY(wrapXY);
10586             }
10587         }
10588         return wrap;
10589     },
10590
10591         /* @private */
10592     fxUnwrap : function(wrap, pos, o){
10593         this.clearPositioning();
10594         this.setPositioning(pos);
10595         if(!o.wrap){
10596             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10597             wrap.remove();
10598         }
10599     },
10600
10601         /* @private */
10602     getFxRestore : function(){
10603         var st = this.dom.style;
10604         return {pos: this.getPositioning(), width: st.width, height : st.height};
10605     },
10606
10607         /* @private */
10608     afterFx : function(o){
10609         if(o.afterStyle){
10610             this.applyStyles(o.afterStyle);
10611         }
10612         if(o.afterCls){
10613             this.addClass(o.afterCls);
10614         }
10615         if(o.remove === true){
10616             this.remove();
10617         }
10618         Roo.callback(o.callback, o.scope, [this]);
10619         if(!o.concurrent){
10620             this.fxQueue.shift();
10621             this.nextFx();
10622         }
10623     },
10624
10625         /* @private */
10626     getFxEl : function(){ // support for composite element fx
10627         return Roo.get(this.dom);
10628     },
10629
10630         /* @private */
10631     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10632         animType = animType || 'run';
10633         opt = opt || {};
10634         var anim = Roo.lib.Anim[animType](
10635             this.dom, args,
10636             (opt.duration || defaultDur) || .35,
10637             (opt.easing || defaultEase) || 'easeOut',
10638             function(){
10639                 Roo.callback(cb, this);
10640             },
10641             this
10642         );
10643         opt.anim = anim;
10644         return anim;
10645     }
10646 };
10647
10648 // backwords compat
10649 Roo.Fx.resize = Roo.Fx.scale;
10650
10651 //When included, Roo.Fx is automatically applied to Element so that all basic
10652 //effects are available directly via the Element API
10653 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10654  * Based on:
10655  * Ext JS Library 1.1.1
10656  * Copyright(c) 2006-2007, Ext JS, LLC.
10657  *
10658  * Originally Released Under LGPL - original licence link has changed is not relivant.
10659  *
10660  * Fork - LGPL
10661  * <script type="text/javascript">
10662  */
10663
10664
10665 /**
10666  * @class Roo.CompositeElement
10667  * Standard composite class. Creates a Roo.Element for every element in the collection.
10668  * <br><br>
10669  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10670  * actions will be performed on all the elements in this collection.</b>
10671  * <br><br>
10672  * All methods return <i>this</i> and can be chained.
10673  <pre><code>
10674  var els = Roo.select("#some-el div.some-class", true);
10675  // or select directly from an existing element
10676  var el = Roo.get('some-el');
10677  el.select('div.some-class', true);
10678
10679  els.setWidth(100); // all elements become 100 width
10680  els.hide(true); // all elements fade out and hide
10681  // or
10682  els.setWidth(100).hide(true);
10683  </code></pre>
10684  */
10685 Roo.CompositeElement = function(els){
10686     this.elements = [];
10687     this.addElements(els);
10688 };
10689 Roo.CompositeElement.prototype = {
10690     isComposite: true,
10691     addElements : function(els){
10692         if(!els) return this;
10693         if(typeof els == "string"){
10694             els = Roo.Element.selectorFunction(els);
10695         }
10696         var yels = this.elements;
10697         var index = yels.length-1;
10698         for(var i = 0, len = els.length; i < len; i++) {
10699                 yels[++index] = Roo.get(els[i]);
10700         }
10701         return this;
10702     },
10703
10704     /**
10705     * Clears this composite and adds the elements returned by the passed selector.
10706     * @param {String/Array} els A string CSS selector, an array of elements or an element
10707     * @return {CompositeElement} this
10708     */
10709     fill : function(els){
10710         this.elements = [];
10711         this.add(els);
10712         return this;
10713     },
10714
10715     /**
10716     * Filters this composite to only elements that match the passed selector.
10717     * @param {String} selector A string CSS selector
10718     * @return {CompositeElement} this
10719     */
10720     filter : function(selector){
10721         var els = [];
10722         this.each(function(el){
10723             if(el.is(selector)){
10724                 els[els.length] = el.dom;
10725             }
10726         });
10727         this.fill(els);
10728         return this;
10729     },
10730
10731     invoke : function(fn, args){
10732         var els = this.elements;
10733         for(var i = 0, len = els.length; i < len; i++) {
10734                 Roo.Element.prototype[fn].apply(els[i], args);
10735         }
10736         return this;
10737     },
10738     /**
10739     * Adds elements to this composite.
10740     * @param {String/Array} els A string CSS selector, an array of elements or an element
10741     * @return {CompositeElement} this
10742     */
10743     add : function(els){
10744         if(typeof els == "string"){
10745             this.addElements(Roo.Element.selectorFunction(els));
10746         }else if(els.length !== undefined){
10747             this.addElements(els);
10748         }else{
10749             this.addElements([els]);
10750         }
10751         return this;
10752     },
10753     /**
10754     * Calls the passed function passing (el, this, index) for each element in this composite.
10755     * @param {Function} fn The function to call
10756     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10757     * @return {CompositeElement} this
10758     */
10759     each : function(fn, scope){
10760         var els = this.elements;
10761         for(var i = 0, len = els.length; i < len; i++){
10762             if(fn.call(scope || els[i], els[i], this, i) === false) {
10763                 break;
10764             }
10765         }
10766         return this;
10767     },
10768
10769     /**
10770      * Returns the Element object at the specified index
10771      * @param {Number} index
10772      * @return {Roo.Element}
10773      */
10774     item : function(index){
10775         return this.elements[index] || null;
10776     },
10777
10778     /**
10779      * Returns the first Element
10780      * @return {Roo.Element}
10781      */
10782     first : function(){
10783         return this.item(0);
10784     },
10785
10786     /**
10787      * Returns the last Element
10788      * @return {Roo.Element}
10789      */
10790     last : function(){
10791         return this.item(this.elements.length-1);
10792     },
10793
10794     /**
10795      * Returns the number of elements in this composite
10796      * @return Number
10797      */
10798     getCount : function(){
10799         return this.elements.length;
10800     },
10801
10802     /**
10803      * Returns true if this composite contains the passed element
10804      * @return Boolean
10805      */
10806     contains : function(el){
10807         return this.indexOf(el) !== -1;
10808     },
10809
10810     /**
10811      * Returns true if this composite contains the passed element
10812      * @return Boolean
10813      */
10814     indexOf : function(el){
10815         return this.elements.indexOf(Roo.get(el));
10816     },
10817
10818
10819     /**
10820     * Removes the specified element(s).
10821     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10822     * or an array of any of those.
10823     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10824     * @return {CompositeElement} this
10825     */
10826     removeElement : function(el, removeDom){
10827         if(el instanceof Array){
10828             for(var i = 0, len = el.length; i < len; i++){
10829                 this.removeElement(el[i]);
10830             }
10831             return this;
10832         }
10833         var index = typeof el == 'number' ? el : this.indexOf(el);
10834         if(index !== -1){
10835             if(removeDom){
10836                 var d = this.elements[index];
10837                 if(d.dom){
10838                     d.remove();
10839                 }else{
10840                     d.parentNode.removeChild(d);
10841                 }
10842             }
10843             this.elements.splice(index, 1);
10844         }
10845         return this;
10846     },
10847
10848     /**
10849     * Replaces the specified element with the passed element.
10850     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10851     * to replace.
10852     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10853     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10854     * @return {CompositeElement} this
10855     */
10856     replaceElement : function(el, replacement, domReplace){
10857         var index = typeof el == 'number' ? el : this.indexOf(el);
10858         if(index !== -1){
10859             if(domReplace){
10860                 this.elements[index].replaceWith(replacement);
10861             }else{
10862                 this.elements.splice(index, 1, Roo.get(replacement))
10863             }
10864         }
10865         return this;
10866     },
10867
10868     /**
10869      * Removes all elements.
10870      */
10871     clear : function(){
10872         this.elements = [];
10873     }
10874 };
10875 (function(){
10876     Roo.CompositeElement.createCall = function(proto, fnName){
10877         if(!proto[fnName]){
10878             proto[fnName] = function(){
10879                 return this.invoke(fnName, arguments);
10880             };
10881         }
10882     };
10883     for(var fnName in Roo.Element.prototype){
10884         if(typeof Roo.Element.prototype[fnName] == "function"){
10885             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10886         }
10887     };
10888 })();
10889 /*
10890  * Based on:
10891  * Ext JS Library 1.1.1
10892  * Copyright(c) 2006-2007, Ext JS, LLC.
10893  *
10894  * Originally Released Under LGPL - original licence link has changed is not relivant.
10895  *
10896  * Fork - LGPL
10897  * <script type="text/javascript">
10898  */
10899
10900 /**
10901  * @class Roo.CompositeElementLite
10902  * @extends Roo.CompositeElement
10903  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class");
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class');
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre><br><br>
10915  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10916  * actions will be performed on all the elements in this collection.</b>
10917  */
10918 Roo.CompositeElementLite = function(els){
10919     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10920     this.el = new Roo.Element.Flyweight();
10921 };
10922 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10923     addElements : function(els){
10924         if(els){
10925             if(els instanceof Array){
10926                 this.elements = this.elements.concat(els);
10927             }else{
10928                 var yels = this.elements;
10929                 var index = yels.length-1;
10930                 for(var i = 0, len = els.length; i < len; i++) {
10931                     yels[++index] = els[i];
10932                 }
10933             }
10934         }
10935         return this;
10936     },
10937     invoke : function(fn, args){
10938         var els = this.elements;
10939         var el = this.el;
10940         for(var i = 0, len = els.length; i < len; i++) {
10941             el.dom = els[i];
10942                 Roo.Element.prototype[fn].apply(el, args);
10943         }
10944         return this;
10945     },
10946     /**
10947      * Returns a flyweight Element of the dom element object at the specified index
10948      * @param {Number} index
10949      * @return {Roo.Element}
10950      */
10951     item : function(index){
10952         if(!this.elements[index]){
10953             return null;
10954         }
10955         this.el.dom = this.elements[index];
10956         return this.el;
10957     },
10958
10959     // fixes scope with flyweight
10960     addListener : function(eventName, handler, scope, opt){
10961         var els = this.elements;
10962         for(var i = 0, len = els.length; i < len; i++) {
10963             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10970     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10971     * a reference to the dom node, use el.dom.</b>
10972     * @param {Function} fn The function to call
10973     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10974     * @return {CompositeElement} this
10975     */
10976     each : function(fn, scope){
10977         var els = this.elements;
10978         var el = this.el;
10979         for(var i = 0, len = els.length; i < len; i++){
10980             el.dom = els[i];
10981                 if(fn.call(scope || el, el, this, i) === false){
10982                 break;
10983             }
10984         }
10985         return this;
10986     },
10987
10988     indexOf : function(el){
10989         return this.elements.indexOf(Roo.getDom(el));
10990     },
10991
10992     replaceElement : function(el, replacement, domReplace){
10993         var index = typeof el == 'number' ? el : this.indexOf(el);
10994         if(index !== -1){
10995             replacement = Roo.getDom(replacement);
10996             if(domReplace){
10997                 var d = this.elements[index];
10998                 d.parentNode.insertBefore(replacement, d);
10999                 d.parentNode.removeChild(d);
11000             }
11001             this.elements.splice(index, 1, replacement);
11002         }
11003         return this;
11004     }
11005 });
11006 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11007
11008 /*
11009  * Based on:
11010  * Ext JS Library 1.1.1
11011  * Copyright(c) 2006-2007, Ext JS, LLC.
11012  *
11013  * Originally Released Under LGPL - original licence link has changed is not relivant.
11014  *
11015  * Fork - LGPL
11016  * <script type="text/javascript">
11017  */
11018
11019  
11020
11021 /**
11022  * @class Roo.data.Connection
11023  * @extends Roo.util.Observable
11024  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11025  * either to a configured URL, or to a URL specified at request time.<br><br>
11026  * <p>
11027  * Requests made by this class are asynchronous, and will return immediately. No data from
11028  * the server will be available to the statement immediately following the {@link #request} call.
11029  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11030  * <p>
11031  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11032  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11033  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11034  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11035  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11036  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11037  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11038  * standard DOM methods.
11039  * @constructor
11040  * @param {Object} config a configuration object.
11041  */
11042 Roo.data.Connection = function(config){
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046          * @event beforerequest
11047          * Fires before a network request is made to retrieve a data object.
11048          * @param {Connection} conn This Connection object.
11049          * @param {Object} options The options config object passed to the {@link #request} method.
11050          */
11051         "beforerequest" : true,
11052         /**
11053          * @event requestcomplete
11054          * Fires if the request was successfully completed.
11055          * @param {Connection} conn This Connection object.
11056          * @param {Object} response The XHR object containing the response data.
11057          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11058          * @param {Object} options The options config object passed to the {@link #request} method.
11059          */
11060         "requestcomplete" : true,
11061         /**
11062          * @event requestexception
11063          * Fires if an error HTTP status was returned from the server.
11064          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11065          * @param {Connection} conn This Connection object.
11066          * @param {Object} response The XHR object containing the response data.
11067          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11068          * @param {Object} options The options config object passed to the {@link #request} method.
11069          */
11070         "requestexception" : true
11071     });
11072     Roo.data.Connection.superclass.constructor.call(this);
11073 };
11074
11075 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11076     /**
11077      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11078      */
11079     /**
11080      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11081      * extra parameters to each request made by this object. (defaults to undefined)
11082      */
11083     /**
11084      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11085      *  to each request made by this object. (defaults to undefined)
11086      */
11087     /**
11088      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11089      */
11090     /**
11091      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11092      */
11093     timeout : 30000,
11094     /**
11095      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11096      * @type Boolean
11097      */
11098     autoAbort:false,
11099
11100     /**
11101      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11102      * @type Boolean
11103      */
11104     disableCaching: true,
11105
11106     /**
11107      * Sends an HTTP request to a remote server.
11108      * @param {Object} options An object which may contain the following properties:<ul>
11109      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11110      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11111      * request, a url encoded string or a function to call to get either.</li>
11112      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11113      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11114      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11115      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11116      * <li>options {Object} The parameter to the request call.</li>
11117      * <li>success {Boolean} True if the request succeeded.</li>
11118      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11119      * </ul></li>
11120      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11121      * The callback is passed the following parameters:<ul>
11122      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11123      * <li>options {Object} The parameter to the request call.</li>
11124      * </ul></li>
11125      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11126      * The callback is passed the following parameters:<ul>
11127      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11128      * <li>options {Object} The parameter to the request call.</li>
11129      * </ul></li>
11130      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11131      * for the callback function. Defaults to the browser window.</li>
11132      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11133      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11134      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11135      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11136      * params for the post data. Any params will be appended to the URL.</li>
11137      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11138      * </ul>
11139      * @return {Number} transactionId
11140      */
11141     request : function(o){
11142         if(this.fireEvent("beforerequest", this, o) !== false){
11143             var p = o.params;
11144
11145             if(typeof p == "function"){
11146                 p = p.call(o.scope||window, o);
11147             }
11148             if(typeof p == "object"){
11149                 p = Roo.urlEncode(o.params);
11150             }
11151             if(this.extraParams){
11152                 var extras = Roo.urlEncode(this.extraParams);
11153                 p = p ? (p + '&' + extras) : extras;
11154             }
11155
11156             var url = o.url || this.url;
11157             if(typeof url == 'function'){
11158                 url = url.call(o.scope||window, o);
11159             }
11160
11161             if(o.form){
11162                 var form = Roo.getDom(o.form);
11163                 url = url || form.action;
11164
11165                 var enctype = form.getAttribute("enctype");
11166                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11167                     return this.doFormUpload(o, p, url);
11168                 }
11169                 var f = Roo.lib.Ajax.serializeForm(form);
11170                 p = p ? (p + '&' + f) : f;
11171             }
11172
11173             var hs = o.headers;
11174             if(this.defaultHeaders){
11175                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11176                 if(!o.headers){
11177                     o.headers = hs;
11178                 }
11179             }
11180
11181             var cb = {
11182                 success: this.handleResponse,
11183                 failure: this.handleFailure,
11184                 scope: this,
11185                 argument: {options: o},
11186                 timeout : this.timeout
11187             };
11188
11189             var method = o.method||this.method||(p ? "POST" : "GET");
11190
11191             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11192                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11193             }
11194
11195             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11196                 if(o.autoAbort){
11197                     this.abort();
11198                 }
11199             }else if(this.autoAbort !== false){
11200                 this.abort();
11201             }
11202
11203             if((method == 'GET' && p) || o.xmlData){
11204                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11205                 p = '';
11206             }
11207             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11208             return this.transId;
11209         }else{
11210             Roo.callback(o.callback, o.scope, [o, null, null]);
11211             return null;
11212         }
11213     },
11214
11215     /**
11216      * Determine whether this object has a request outstanding.
11217      * @param {Number} transactionId (Optional) defaults to the last transaction
11218      * @return {Boolean} True if there is an outstanding request.
11219      */
11220     isLoading : function(transId){
11221         if(transId){
11222             return Roo.lib.Ajax.isCallInProgress(transId);
11223         }else{
11224             return this.transId ? true : false;
11225         }
11226     },
11227
11228     /**
11229      * Aborts any outstanding request.
11230      * @param {Number} transactionId (Optional) defaults to the last transaction
11231      */
11232     abort : function(transId){
11233         if(transId || this.isLoading()){
11234             Roo.lib.Ajax.abort(transId || this.transId);
11235         }
11236     },
11237
11238     // private
11239     handleResponse : function(response){
11240         this.transId = false;
11241         var options = response.argument.options;
11242         response.argument = options ? options.argument : null;
11243         this.fireEvent("requestcomplete", this, response, options);
11244         Roo.callback(options.success, options.scope, [response, options]);
11245         Roo.callback(options.callback, options.scope, [options, true, response]);
11246     },
11247
11248     // private
11249     handleFailure : function(response, e){
11250         this.transId = false;
11251         var options = response.argument.options;
11252         response.argument = options ? options.argument : null;
11253         this.fireEvent("requestexception", this, response, options, e);
11254         Roo.callback(options.failure, options.scope, [response, options]);
11255         Roo.callback(options.callback, options.scope, [options, false, response]);
11256     },
11257
11258     // private
11259     doFormUpload : function(o, ps, url){
11260         var id = Roo.id();
11261         var frame = document.createElement('iframe');
11262         frame.id = id;
11263         frame.name = id;
11264         frame.className = 'x-hidden';
11265         if(Roo.isIE){
11266             frame.src = Roo.SSL_SECURE_URL;
11267         }
11268         document.body.appendChild(frame);
11269
11270         if(Roo.isIE){
11271            document.frames[id].name = id;
11272         }
11273
11274         var form = Roo.getDom(o.form);
11275         form.target = id;
11276         form.method = 'POST';
11277         form.enctype = form.encoding = 'multipart/form-data';
11278         if(url){
11279             form.action = url;
11280         }
11281
11282         var hiddens, hd;
11283         if(ps){ // add dynamic params
11284             hiddens = [];
11285             ps = Roo.urlDecode(ps, false);
11286             for(var k in ps){
11287                 if(ps.hasOwnProperty(k)){
11288                     hd = document.createElement('input');
11289                     hd.type = 'hidden';
11290                     hd.name = k;
11291                     hd.value = ps[k];
11292                     form.appendChild(hd);
11293                     hiddens.push(hd);
11294                 }
11295             }
11296         }
11297
11298         function cb(){
11299             var r = {  // bogus response object
11300                 responseText : '',
11301                 responseXML : null
11302             };
11303
11304             r.argument = o ? o.argument : null;
11305
11306             try { //
11307                 var doc;
11308                 if(Roo.isIE){
11309                     doc = frame.contentWindow.document;
11310                 }else {
11311                     doc = (frame.contentDocument || window.frames[id].document);
11312                 }
11313                 if(doc && doc.body){
11314                     r.responseText = doc.body.innerHTML;
11315                 }
11316                 if(doc && doc.XMLDocument){
11317                     r.responseXML = doc.XMLDocument;
11318                 }else {
11319                     r.responseXML = doc;
11320                 }
11321             }
11322             catch(e) {
11323                 // ignore
11324             }
11325
11326             Roo.EventManager.removeListener(frame, 'load', cb, this);
11327
11328             this.fireEvent("requestcomplete", this, r, o);
11329             Roo.callback(o.success, o.scope, [r, o]);
11330             Roo.callback(o.callback, o.scope, [o, true, r]);
11331
11332             setTimeout(function(){document.body.removeChild(frame);}, 100);
11333         }
11334
11335         Roo.EventManager.on(frame, 'load', cb, this);
11336         form.submit();
11337
11338         if(hiddens){ // remove dynamic params
11339             for(var i = 0, len = hiddens.length; i < len; i++){
11340                 form.removeChild(hiddens[i]);
11341             }
11342         }
11343     }
11344 });
11345
11346 /**
11347  * @class Roo.Ajax
11348  * @extends Roo.data.Connection
11349  * Global Ajax request class.
11350  *
11351  * @singleton
11352  */
11353 Roo.Ajax = new Roo.data.Connection({
11354     // fix up the docs
11355    /**
11356      * @cfg {String} url @hide
11357      */
11358     /**
11359      * @cfg {Object} extraParams @hide
11360      */
11361     /**
11362      * @cfg {Object} defaultHeaders @hide
11363      */
11364     /**
11365      * @cfg {String} method (Optional) @hide
11366      */
11367     /**
11368      * @cfg {Number} timeout (Optional) @hide
11369      */
11370     /**
11371      * @cfg {Boolean} autoAbort (Optional) @hide
11372      */
11373
11374     /**
11375      * @cfg {Boolean} disableCaching (Optional) @hide
11376      */
11377
11378     /**
11379      * @property  disableCaching
11380      * True to add a unique cache-buster param to GET requests. (defaults to true)
11381      * @type Boolean
11382      */
11383     /**
11384      * @property  url
11385      * The default URL to be used for requests to the server. (defaults to undefined)
11386      * @type String
11387      */
11388     /**
11389      * @property  extraParams
11390      * An object containing properties which are used as
11391      * extra parameters to each request made by this object. (defaults to undefined)
11392      * @type Object
11393      */
11394     /**
11395      * @property  defaultHeaders
11396      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11397      * @type Object
11398      */
11399     /**
11400      * @property  method
11401      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11402      * @type String
11403      */
11404     /**
11405      * @property  timeout
11406      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11407      * @type Number
11408      */
11409
11410     /**
11411      * @property  autoAbort
11412      * Whether a new request should abort any pending requests. (defaults to false)
11413      * @type Boolean
11414      */
11415     autoAbort : false,
11416
11417     /**
11418      * Serialize the passed form into a url encoded string
11419      * @param {String/HTMLElement} form
11420      * @return {String}
11421      */
11422     serializeForm : function(form){
11423         return Roo.lib.Ajax.serializeForm(form);
11424     }
11425 });/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435  
11436 /**
11437  * @class Roo.Ajax
11438  * @extends Roo.data.Connection
11439  * Global Ajax request class.
11440  *
11441  * @instanceOf  Roo.data.Connection
11442  */
11443 Roo.Ajax = new Roo.data.Connection({
11444     // fix up the docs
11445     
11446     /**
11447      * fix up scoping
11448      * @scope Roo.Ajax
11449      */
11450     
11451    /**
11452      * @cfg {String} url @hide
11453      */
11454     /**
11455      * @cfg {Object} extraParams @hide
11456      */
11457     /**
11458      * @cfg {Object} defaultHeaders @hide
11459      */
11460     /**
11461      * @cfg {String} method (Optional) @hide
11462      */
11463     /**
11464      * @cfg {Number} timeout (Optional) @hide
11465      */
11466     /**
11467      * @cfg {Boolean} autoAbort (Optional) @hide
11468      */
11469
11470     /**
11471      * @cfg {Boolean} disableCaching (Optional) @hide
11472      */
11473
11474     /**
11475      * @property  disableCaching
11476      * True to add a unique cache-buster param to GET requests. (defaults to true)
11477      * @type Boolean
11478      */
11479     /**
11480      * @property  url
11481      * The default URL to be used for requests to the server. (defaults to undefined)
11482      * @type String
11483      */
11484     /**
11485      * @property  extraParams
11486      * An object containing properties which are used as
11487      * extra parameters to each request made by this object. (defaults to undefined)
11488      * @type Object
11489      */
11490     /**
11491      * @property  defaultHeaders
11492      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11493      * @type Object
11494      */
11495     /**
11496      * @property  method
11497      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11498      * @type String
11499      */
11500     /**
11501      * @property  timeout
11502      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11503      * @type Number
11504      */
11505
11506     /**
11507      * @property  autoAbort
11508      * Whether a new request should abort any pending requests. (defaults to false)
11509      * @type Boolean
11510      */
11511     autoAbort : false,
11512
11513     /**
11514      * Serialize the passed form into a url encoded string
11515      * @param {String/HTMLElement} form
11516      * @return {String}
11517      */
11518     serializeForm : function(form){
11519         return Roo.lib.Ajax.serializeForm(form);
11520     }
11521 });/*
11522  * Based on:
11523  * Ext JS Library 1.1.1
11524  * Copyright(c) 2006-2007, Ext JS, LLC.
11525  *
11526  * Originally Released Under LGPL - original licence link has changed is not relivant.
11527  *
11528  * Fork - LGPL
11529  * <script type="text/javascript">
11530  */
11531
11532  
11533 /**
11534  * @class Roo.UpdateManager
11535  * @extends Roo.util.Observable
11536  * Provides AJAX-style update for Element object.<br><br>
11537  * Usage:<br>
11538  * <pre><code>
11539  * // Get it from a Roo.Element object
11540  * var el = Roo.get("foo");
11541  * var mgr = el.getUpdateManager();
11542  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11543  * ...
11544  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11545  * <br>
11546  * // or directly (returns the same UpdateManager instance)
11547  * var mgr = new Roo.UpdateManager("myElementId");
11548  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11549  * mgr.on("update", myFcnNeedsToKnow);
11550  * <br>
11551    // short handed call directly from the element object
11552    Roo.get("foo").load({
11553         url: "bar.php",
11554         scripts:true,
11555         params: "for=bar",
11556         text: "Loading Foo..."
11557    });
11558  * </code></pre>
11559  * @constructor
11560  * Create new UpdateManager directly.
11561  * @param {String/HTMLElement/Roo.Element} el The element to update
11562  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11563  */
11564 Roo.UpdateManager = function(el, forceNew){
11565     el = Roo.get(el);
11566     if(!forceNew && el.updateManager){
11567         return el.updateManager;
11568     }
11569     /**
11570      * The Element object
11571      * @type Roo.Element
11572      */
11573     this.el = el;
11574     /**
11575      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11576      * @type String
11577      */
11578     this.defaultUrl = null;
11579
11580     this.addEvents({
11581         /**
11582          * @event beforeupdate
11583          * Fired before an update is made, return false from your handler and the update is cancelled.
11584          * @param {Roo.Element} el
11585          * @param {String/Object/Function} url
11586          * @param {String/Object} params
11587          */
11588         "beforeupdate": true,
11589         /**
11590          * @event update
11591          * Fired after successful update is made.
11592          * @param {Roo.Element} el
11593          * @param {Object} oResponseObject The response Object
11594          */
11595         "update": true,
11596         /**
11597          * @event failure
11598          * Fired on update failure.
11599          * @param {Roo.Element} el
11600          * @param {Object} oResponseObject The response Object
11601          */
11602         "failure": true
11603     });
11604     var d = Roo.UpdateManager.defaults;
11605     /**
11606      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11607      * @type String
11608      */
11609     this.sslBlankUrl = d.sslBlankUrl;
11610     /**
11611      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11612      * @type Boolean
11613      */
11614     this.disableCaching = d.disableCaching;
11615     /**
11616      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11617      * @type String
11618      */
11619     this.indicatorText = d.indicatorText;
11620     /**
11621      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11622      * @type String
11623      */
11624     this.showLoadIndicator = d.showLoadIndicator;
11625     /**
11626      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11627      * @type Number
11628      */
11629     this.timeout = d.timeout;
11630
11631     /**
11632      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11633      * @type Boolean
11634      */
11635     this.loadScripts = d.loadScripts;
11636
11637     /**
11638      * Transaction object of current executing transaction
11639      */
11640     this.transaction = null;
11641
11642     /**
11643      * @private
11644      */
11645     this.autoRefreshProcId = null;
11646     /**
11647      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11648      * @type Function
11649      */
11650     this.refreshDelegate = this.refresh.createDelegate(this);
11651     /**
11652      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11653      * @type Function
11654      */
11655     this.updateDelegate = this.update.createDelegate(this);
11656     /**
11657      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11658      * @type Function
11659      */
11660     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11661     /**
11662      * @private
11663      */
11664     this.successDelegate = this.processSuccess.createDelegate(this);
11665     /**
11666      * @private
11667      */
11668     this.failureDelegate = this.processFailure.createDelegate(this);
11669
11670     if(!this.renderer){
11671      /**
11672       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11673       */
11674     this.renderer = new Roo.UpdateManager.BasicRenderer();
11675     }
11676     
11677     Roo.UpdateManager.superclass.constructor.call(this);
11678 };
11679
11680 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11681     /**
11682      * Get the Element this UpdateManager is bound to
11683      * @return {Roo.Element} The element
11684      */
11685     getEl : function(){
11686         return this.el;
11687     },
11688     /**
11689      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11690      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11691 <pre><code>
11692 um.update({<br/>
11693     url: "your-url.php",<br/>
11694     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11695     callback: yourFunction,<br/>
11696     scope: yourObject, //(optional scope)  <br/>
11697     discardUrl: false, <br/>
11698     nocache: false,<br/>
11699     text: "Loading...",<br/>
11700     timeout: 30,<br/>
11701     scripts: false<br/>
11702 });
11703 </code></pre>
11704      * The only required property is url. The optional properties nocache, text and scripts
11705      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11706      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11707      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11708      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11709      */
11710     update : function(url, params, callback, discardUrl){
11711         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11712             var method = this.method, cfg;
11713             if(typeof url == "object"){ // must be config object
11714                 cfg = url;
11715                 url = cfg.url;
11716                 params = params || cfg.params;
11717                 callback = callback || cfg.callback;
11718                 discardUrl = discardUrl || cfg.discardUrl;
11719                 if(callback && cfg.scope){
11720                     callback = callback.createDelegate(cfg.scope);
11721                 }
11722                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11723                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11724                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11725                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11726                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11727             }
11728             this.showLoading();
11729             if(!discardUrl){
11730                 this.defaultUrl = url;
11731             }
11732             if(typeof url == "function"){
11733                 url = url.call(this);
11734             }
11735
11736             method = method || (params ? "POST" : "GET");
11737             if(method == "GET"){
11738                 url = this.prepareUrl(url);
11739             }
11740
11741             var o = Roo.apply(cfg ||{}, {
11742                 url : url,
11743                 params: params,
11744                 success: this.successDelegate,
11745                 failure: this.failureDelegate,
11746                 callback: undefined,
11747                 timeout: (this.timeout*1000),
11748                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11749             });
11750
11751             this.transaction = Roo.Ajax.request(o);
11752         }
11753     },
11754
11755     /**
11756      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11757      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11758      * @param {String/HTMLElement} form The form Id or form element
11759      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11760      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11761      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11762      */
11763     formUpdate : function(form, url, reset, callback){
11764         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11765             if(typeof url == "function"){
11766                 url = url.call(this);
11767             }
11768             form = Roo.getDom(form);
11769             this.transaction = Roo.Ajax.request({
11770                 form: form,
11771                 url:url,
11772                 success: this.successDelegate,
11773                 failure: this.failureDelegate,
11774                 timeout: (this.timeout*1000),
11775                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11776             });
11777             this.showLoading.defer(1, this);
11778         }
11779     },
11780
11781     /**
11782      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11783      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11784      */
11785     refresh : function(callback){
11786         if(this.defaultUrl == null){
11787             return;
11788         }
11789         this.update(this.defaultUrl, null, callback, true);
11790     },
11791
11792     /**
11793      * Set this element to auto refresh.
11794      * @param {Number} interval How often to update (in seconds).
11795      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11796      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11798      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11799      */
11800     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11801         if(refreshNow){
11802             this.update(url || this.defaultUrl, params, callback, true);
11803         }
11804         if(this.autoRefreshProcId){
11805             clearInterval(this.autoRefreshProcId);
11806         }
11807         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11808     },
11809
11810     /**
11811      * Stop auto refresh on this element.
11812      */
11813      stopAutoRefresh : function(){
11814         if(this.autoRefreshProcId){
11815             clearInterval(this.autoRefreshProcId);
11816             delete this.autoRefreshProcId;
11817         }
11818     },
11819
11820     isAutoRefreshing : function(){
11821        return this.autoRefreshProcId ? true : false;
11822     },
11823     /**
11824      * Called to update the element to "Loading" state. Override to perform custom action.
11825      */
11826     showLoading : function(){
11827         if(this.showLoadIndicator){
11828             this.el.update(this.indicatorText);
11829         }
11830     },
11831
11832     /**
11833      * Adds unique parameter to query string if disableCaching = true
11834      * @private
11835      */
11836     prepareUrl : function(url){
11837         if(this.disableCaching){
11838             var append = "_dc=" + (new Date().getTime());
11839             if(url.indexOf("?") !== -1){
11840                 url += "&" + append;
11841             }else{
11842                 url += "?" + append;
11843             }
11844         }
11845         return url;
11846     },
11847
11848     /**
11849      * @private
11850      */
11851     processSuccess : function(response){
11852         this.transaction = null;
11853         if(response.argument.form && response.argument.reset){
11854             try{ // put in try/catch since some older FF releases had problems with this
11855                 response.argument.form.reset();
11856             }catch(e){}
11857         }
11858         if(this.loadScripts){
11859             this.renderer.render(this.el, response, this,
11860                 this.updateComplete.createDelegate(this, [response]));
11861         }else{
11862             this.renderer.render(this.el, response, this);
11863             this.updateComplete(response);
11864         }
11865     },
11866
11867     updateComplete : function(response){
11868         this.fireEvent("update", this.el, response);
11869         if(typeof response.argument.callback == "function"){
11870             response.argument.callback(this.el, true, response);
11871         }
11872     },
11873
11874     /**
11875      * @private
11876      */
11877     processFailure : function(response){
11878         this.transaction = null;
11879         this.fireEvent("failure", this.el, response);
11880         if(typeof response.argument.callback == "function"){
11881             response.argument.callback(this.el, false, response);
11882         }
11883     },
11884
11885     /**
11886      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11887      * @param {Object} renderer The object implementing the render() method
11888      */
11889     setRenderer : function(renderer){
11890         this.renderer = renderer;
11891     },
11892
11893     getRenderer : function(){
11894        return this.renderer;
11895     },
11896
11897     /**
11898      * Set the defaultUrl used for updates
11899      * @param {String/Function} defaultUrl The url or a function to call to get the url
11900      */
11901     setDefaultUrl : function(defaultUrl){
11902         this.defaultUrl = defaultUrl;
11903     },
11904
11905     /**
11906      * Aborts the executing transaction
11907      */
11908     abort : function(){
11909         if(this.transaction){
11910             Roo.Ajax.abort(this.transaction);
11911         }
11912     },
11913
11914     /**
11915      * Returns true if an update is in progress
11916      * @return {Boolean}
11917      */
11918     isUpdating : function(){
11919         if(this.transaction){
11920             return Roo.Ajax.isLoading(this.transaction);
11921         }
11922         return false;
11923     }
11924 });
11925
11926 /**
11927  * @class Roo.UpdateManager.defaults
11928  * @static (not really - but it helps the doc tool)
11929  * The defaults collection enables customizing the default properties of UpdateManager
11930  */
11931    Roo.UpdateManager.defaults = {
11932        /**
11933          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11934          * @type Number
11935          */
11936          timeout : 30,
11937
11938          /**
11939          * True to process scripts by default (Defaults to false).
11940          * @type Boolean
11941          */
11942         loadScripts : false,
11943
11944         /**
11945         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11946         * @type String
11947         */
11948         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11949         /**
11950          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11951          * @type Boolean
11952          */
11953         disableCaching : false,
11954         /**
11955          * Whether to show indicatorText when loading (Defaults to true).
11956          * @type Boolean
11957          */
11958         showLoadIndicator : true,
11959         /**
11960          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11961          * @type String
11962          */
11963         indicatorText : '<div class="loading-indicator">Loading...</div>'
11964    };
11965
11966 /**
11967  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11968  *Usage:
11969  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11970  * @param {String/HTMLElement/Roo.Element} el The element to update
11971  * @param {String} url The url
11972  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11973  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11974  * @static
11975  * @deprecated
11976  * @member Roo.UpdateManager
11977  */
11978 Roo.UpdateManager.updateElement = function(el, url, params, options){
11979     var um = Roo.get(el, true).getUpdateManager();
11980     Roo.apply(um, options);
11981     um.update(url, params, options ? options.callback : null);
11982 };
11983 // alias for backwards compat
11984 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11985 /**
11986  * @class Roo.UpdateManager.BasicRenderer
11987  * Default Content renderer. Updates the elements innerHTML with the responseText.
11988  */
11989 Roo.UpdateManager.BasicRenderer = function(){};
11990
11991 Roo.UpdateManager.BasicRenderer.prototype = {
11992     /**
11993      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11994      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11995      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11996      * @param {Roo.Element} el The element being rendered
11997      * @param {Object} response The YUI Connect response object
11998      * @param {UpdateManager} updateManager The calling update manager
11999      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12000      */
12001      render : function(el, response, updateManager, callback){
12002         el.update(response.responseText, updateManager.loadScripts, callback);
12003     }
12004 };
12005 /*
12006  * Based on:
12007  * Ext JS Library 1.1.1
12008  * Copyright(c) 2006-2007, Ext JS, LLC.
12009  *
12010  * Originally Released Under LGPL - original licence link has changed is not relivant.
12011  *
12012  * Fork - LGPL
12013  * <script type="text/javascript">
12014  */
12015
12016 /**
12017  * @class Roo.util.DelayedTask
12018  * Provides a convenient method of performing setTimeout where a new
12019  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12020  * You can use this class to buffer
12021  * the keypress events for a certain number of milliseconds, and perform only if they stop
12022  * for that amount of time.
12023  * @constructor The parameters to this constructor serve as defaults and are not required.
12024  * @param {Function} fn (optional) The default function to timeout
12025  * @param {Object} scope (optional) The default scope of that timeout
12026  * @param {Array} args (optional) The default Array of arguments
12027  */
12028 Roo.util.DelayedTask = function(fn, scope, args){
12029     var id = null, d, t;
12030
12031     var call = function(){
12032         var now = new Date().getTime();
12033         if(now - t >= d){
12034             clearInterval(id);
12035             id = null;
12036             fn.apply(scope, args || []);
12037         }
12038     };
12039     /**
12040      * Cancels any pending timeout and queues a new one
12041      * @param {Number} delay The milliseconds to delay
12042      * @param {Function} newFn (optional) Overrides function passed to constructor
12043      * @param {Object} newScope (optional) Overrides scope passed to constructor
12044      * @param {Array} newArgs (optional) Overrides args passed to constructor
12045      */
12046     this.delay = function(delay, newFn, newScope, newArgs){
12047         if(id && delay != d){
12048             this.cancel();
12049         }
12050         d = delay;
12051         t = new Date().getTime();
12052         fn = newFn || fn;
12053         scope = newScope || scope;
12054         args = newArgs || args;
12055         if(!id){
12056             id = setInterval(call, d);
12057         }
12058     };
12059
12060     /**
12061      * Cancel the last queued timeout
12062      */
12063     this.cancel = function(){
12064         if(id){
12065             clearInterval(id);
12066             id = null;
12067         }
12068     };
12069 };/*
12070  * Based on:
12071  * Ext JS Library 1.1.1
12072  * Copyright(c) 2006-2007, Ext JS, LLC.
12073  *
12074  * Originally Released Under LGPL - original licence link has changed is not relivant.
12075  *
12076  * Fork - LGPL
12077  * <script type="text/javascript">
12078  */
12079  
12080  
12081 Roo.util.TaskRunner = function(interval){
12082     interval = interval || 10;
12083     var tasks = [], removeQueue = [];
12084     var id = 0;
12085     var running = false;
12086
12087     var stopThread = function(){
12088         running = false;
12089         clearInterval(id);
12090         id = 0;
12091     };
12092
12093     var startThread = function(){
12094         if(!running){
12095             running = true;
12096             id = setInterval(runTasks, interval);
12097         }
12098     };
12099
12100     var removeTask = function(task){
12101         removeQueue.push(task);
12102         if(task.onStop){
12103             task.onStop();
12104         }
12105     };
12106
12107     var runTasks = function(){
12108         if(removeQueue.length > 0){
12109             for(var i = 0, len = removeQueue.length; i < len; i++){
12110                 tasks.remove(removeQueue[i]);
12111             }
12112             removeQueue = [];
12113             if(tasks.length < 1){
12114                 stopThread();
12115                 return;
12116             }
12117         }
12118         var now = new Date().getTime();
12119         for(var i = 0, len = tasks.length; i < len; ++i){
12120             var t = tasks[i];
12121             var itime = now - t.taskRunTime;
12122             if(t.interval <= itime){
12123                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12124                 t.taskRunTime = now;
12125                 if(rt === false || t.taskRunCount === t.repeat){
12126                     removeTask(t);
12127                     return;
12128                 }
12129             }
12130             if(t.duration && t.duration <= (now - t.taskStartTime)){
12131                 removeTask(t);
12132             }
12133         }
12134     };
12135
12136     /**
12137      * Queues a new task.
12138      * @param {Object} task
12139      */
12140     this.start = function(task){
12141         tasks.push(task);
12142         task.taskStartTime = new Date().getTime();
12143         task.taskRunTime = 0;
12144         task.taskRunCount = 0;
12145         startThread();
12146         return task;
12147     };
12148
12149     this.stop = function(task){
12150         removeTask(task);
12151         return task;
12152     };
12153
12154     this.stopAll = function(){
12155         stopThread();
12156         for(var i = 0, len = tasks.length; i < len; i++){
12157             if(tasks[i].onStop){
12158                 tasks[i].onStop();
12159             }
12160         }
12161         tasks = [];
12162         removeQueue = [];
12163     };
12164 };
12165
12166 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12167  * Based on:
12168  * Ext JS Library 1.1.1
12169  * Copyright(c) 2006-2007, Ext JS, LLC.
12170  *
12171  * Originally Released Under LGPL - original licence link has changed is not relivant.
12172  *
12173  * Fork - LGPL
12174  * <script type="text/javascript">
12175  */
12176
12177  
12178 /**
12179  * @class Roo.util.MixedCollection
12180  * @extends Roo.util.Observable
12181  * A Collection class that maintains both numeric indexes and keys and exposes events.
12182  * @constructor
12183  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12184  * collection (defaults to false)
12185  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12186  * and return the key value for that item.  This is used when available to look up the key on items that
12187  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12188  * equivalent to providing an implementation for the {@link #getKey} method.
12189  */
12190 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12191     this.items = [];
12192     this.map = {};
12193     this.keys = [];
12194     this.length = 0;
12195     this.addEvents({
12196         /**
12197          * @event clear
12198          * Fires when the collection is cleared.
12199          */
12200         "clear" : true,
12201         /**
12202          * @event add
12203          * Fires when an item is added to the collection.
12204          * @param {Number} index The index at which the item was added.
12205          * @param {Object} o The item added.
12206          * @param {String} key The key associated with the added item.
12207          */
12208         "add" : true,
12209         /**
12210          * @event replace
12211          * Fires when an item is replaced in the collection.
12212          * @param {String} key he key associated with the new added.
12213          * @param {Object} old The item being replaced.
12214          * @param {Object} new The new item.
12215          */
12216         "replace" : true,
12217         /**
12218          * @event remove
12219          * Fires when an item is removed from the collection.
12220          * @param {Object} o The item being removed.
12221          * @param {String} key (optional) The key associated with the removed item.
12222          */
12223         "remove" : true,
12224         "sort" : true
12225     });
12226     this.allowFunctions = allowFunctions === true;
12227     if(keyFn){
12228         this.getKey = keyFn;
12229     }
12230     Roo.util.MixedCollection.superclass.constructor.call(this);
12231 };
12232
12233 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12234     allowFunctions : false,
12235     
12236 /**
12237  * Adds an item to the collection.
12238  * @param {String} key The key to associate with the item
12239  * @param {Object} o The item to add.
12240  * @return {Object} The item added.
12241  */
12242     add : function(key, o){
12243         if(arguments.length == 1){
12244             o = arguments[0];
12245             key = this.getKey(o);
12246         }
12247         if(typeof key == "undefined" || key === null){
12248             this.length++;
12249             this.items.push(o);
12250             this.keys.push(null);
12251         }else{
12252             var old = this.map[key];
12253             if(old){
12254                 return this.replace(key, o);
12255             }
12256             this.length++;
12257             this.items.push(o);
12258             this.map[key] = o;
12259             this.keys.push(key);
12260         }
12261         this.fireEvent("add", this.length-1, o, key);
12262         return o;
12263     },
12264    
12265 /**
12266   * MixedCollection has a generic way to fetch keys if you implement getKey.
12267 <pre><code>
12268 // normal way
12269 var mc = new Roo.util.MixedCollection();
12270 mc.add(someEl.dom.id, someEl);
12271 mc.add(otherEl.dom.id, otherEl);
12272 //and so on
12273
12274 // using getKey
12275 var mc = new Roo.util.MixedCollection();
12276 mc.getKey = function(el){
12277    return el.dom.id;
12278 };
12279 mc.add(someEl);
12280 mc.add(otherEl);
12281
12282 // or via the constructor
12283 var mc = new Roo.util.MixedCollection(false, function(el){
12284    return el.dom.id;
12285 });
12286 mc.add(someEl);
12287 mc.add(otherEl);
12288 </code></pre>
12289  * @param o {Object} The item for which to find the key.
12290  * @return {Object} The key for the passed item.
12291  */
12292     getKey : function(o){
12293          return o.id; 
12294     },
12295    
12296 /**
12297  * Replaces an item in the collection.
12298  * @param {String} key The key associated with the item to replace, or the item to replace.
12299  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12300  * @return {Object}  The new item.
12301  */
12302     replace : function(key, o){
12303         if(arguments.length == 1){
12304             o = arguments[0];
12305             key = this.getKey(o);
12306         }
12307         var old = this.item(key);
12308         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12309              return this.add(key, o);
12310         }
12311         var index = this.indexOfKey(key);
12312         this.items[index] = o;
12313         this.map[key] = o;
12314         this.fireEvent("replace", key, old, o);
12315         return o;
12316     },
12317    
12318 /**
12319  * Adds all elements of an Array or an Object to the collection.
12320  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12321  * an Array of values, each of which are added to the collection.
12322  */
12323     addAll : function(objs){
12324         if(arguments.length > 1 || objs instanceof Array){
12325             var args = arguments.length > 1 ? arguments : objs;
12326             for(var i = 0, len = args.length; i < len; i++){
12327                 this.add(args[i]);
12328             }
12329         }else{
12330             for(var key in objs){
12331                 if(this.allowFunctions || typeof objs[key] != "function"){
12332                     this.add(key, objs[key]);
12333                 }
12334             }
12335         }
12336     },
12337    
12338 /**
12339  * Executes the specified function once for every item in the collection, passing each
12340  * item as the first and only parameter. returning false from the function will stop the iteration.
12341  * @param {Function} fn The function to execute for each item.
12342  * @param {Object} scope (optional) The scope in which to execute the function.
12343  */
12344     each : function(fn, scope){
12345         var items = [].concat(this.items); // each safe for removal
12346         for(var i = 0, len = items.length; i < len; i++){
12347             if(fn.call(scope || items[i], items[i], i, len) === false){
12348                 break;
12349             }
12350         }
12351     },
12352    
12353 /**
12354  * Executes the specified function once for every key in the collection, passing each
12355  * key, and its associated item as the first two parameters.
12356  * @param {Function} fn The function to execute for each item.
12357  * @param {Object} scope (optional) The scope in which to execute the function.
12358  */
12359     eachKey : function(fn, scope){
12360         for(var i = 0, len = this.keys.length; i < len; i++){
12361             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12362         }
12363     },
12364    
12365 /**
12366  * Returns the first item in the collection which elicits a true return value from the
12367  * passed selection function.
12368  * @param {Function} fn The selection function to execute for each item.
12369  * @param {Object} scope (optional) The scope in which to execute the function.
12370  * @return {Object} The first item in the collection which returned true from the selection function.
12371  */
12372     find : function(fn, scope){
12373         for(var i = 0, len = this.items.length; i < len; i++){
12374             if(fn.call(scope || window, this.items[i], this.keys[i])){
12375                 return this.items[i];
12376             }
12377         }
12378         return null;
12379     },
12380    
12381 /**
12382  * Inserts an item at the specified index in the collection.
12383  * @param {Number} index The index to insert the item at.
12384  * @param {String} key The key to associate with the new item, or the item itself.
12385  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12386  * @return {Object} The item inserted.
12387  */
12388     insert : function(index, key, o){
12389         if(arguments.length == 2){
12390             o = arguments[1];
12391             key = this.getKey(o);
12392         }
12393         if(index >= this.length){
12394             return this.add(key, o);
12395         }
12396         this.length++;
12397         this.items.splice(index, 0, o);
12398         if(typeof key != "undefined" && key != null){
12399             this.map[key] = o;
12400         }
12401         this.keys.splice(index, 0, key);
12402         this.fireEvent("add", index, o, key);
12403         return o;
12404     },
12405    
12406 /**
12407  * Removed an item from the collection.
12408  * @param {Object} o The item to remove.
12409  * @return {Object} The item removed.
12410  */
12411     remove : function(o){
12412         return this.removeAt(this.indexOf(o));
12413     },
12414    
12415 /**
12416  * Remove an item from a specified index in the collection.
12417  * @param {Number} index The index within the collection of the item to remove.
12418  */
12419     removeAt : function(index){
12420         if(index < this.length && index >= 0){
12421             this.length--;
12422             var o = this.items[index];
12423             this.items.splice(index, 1);
12424             var key = this.keys[index];
12425             if(typeof key != "undefined"){
12426                 delete this.map[key];
12427             }
12428             this.keys.splice(index, 1);
12429             this.fireEvent("remove", o, key);
12430         }
12431     },
12432    
12433 /**
12434  * Removed an item associated with the passed key fom the collection.
12435  * @param {String} key The key of the item to remove.
12436  */
12437     removeKey : function(key){
12438         return this.removeAt(this.indexOfKey(key));
12439     },
12440    
12441 /**
12442  * Returns the number of items in the collection.
12443  * @return {Number} the number of items in the collection.
12444  */
12445     getCount : function(){
12446         return this.length; 
12447     },
12448    
12449 /**
12450  * Returns index within the collection of the passed Object.
12451  * @param {Object} o The item to find the index of.
12452  * @return {Number} index of the item.
12453  */
12454     indexOf : function(o){
12455         if(!this.items.indexOf){
12456             for(var i = 0, len = this.items.length; i < len; i++){
12457                 if(this.items[i] == o) return i;
12458             }
12459             return -1;
12460         }else{
12461             return this.items.indexOf(o);
12462         }
12463     },
12464    
12465 /**
12466  * Returns index within the collection of the passed key.
12467  * @param {String} key The key to find the index of.
12468  * @return {Number} index of the key.
12469  */
12470     indexOfKey : function(key){
12471         if(!this.keys.indexOf){
12472             for(var i = 0, len = this.keys.length; i < len; i++){
12473                 if(this.keys[i] == key) return i;
12474             }
12475             return -1;
12476         }else{
12477             return this.keys.indexOf(key);
12478         }
12479     },
12480    
12481 /**
12482  * Returns the item associated with the passed key OR index. Key has priority over index.
12483  * @param {String/Number} key The key or index of the item.
12484  * @return {Object} The item associated with the passed key.
12485  */
12486     item : function(key){
12487         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12488         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12489     },
12490     
12491 /**
12492  * Returns the item at the specified index.
12493  * @param {Number} index The index of the item.
12494  * @return {Object}
12495  */
12496     itemAt : function(index){
12497         return this.items[index];
12498     },
12499     
12500 /**
12501  * Returns the item associated with the passed key.
12502  * @param {String/Number} key The key of the item.
12503  * @return {Object} The item associated with the passed key.
12504  */
12505     key : function(key){
12506         return this.map[key];
12507     },
12508    
12509 /**
12510  * Returns true if the collection contains the passed Object as an item.
12511  * @param {Object} o  The Object to look for in the collection.
12512  * @return {Boolean} True if the collection contains the Object as an item.
12513  */
12514     contains : function(o){
12515         return this.indexOf(o) != -1;
12516     },
12517    
12518 /**
12519  * Returns true if the collection contains the passed Object as a key.
12520  * @param {String} key The key to look for in the collection.
12521  * @return {Boolean} True if the collection contains the Object as a key.
12522  */
12523     containsKey : function(key){
12524         return typeof this.map[key] != "undefined";
12525     },
12526    
12527 /**
12528  * Removes all items from the collection.
12529  */
12530     clear : function(){
12531         this.length = 0;
12532         this.items = [];
12533         this.keys = [];
12534         this.map = {};
12535         this.fireEvent("clear");
12536     },
12537    
12538 /**
12539  * Returns the first item in the collection.
12540  * @return {Object} the first item in the collection..
12541  */
12542     first : function(){
12543         return this.items[0]; 
12544     },
12545    
12546 /**
12547  * Returns the last item in the collection.
12548  * @return {Object} the last item in the collection..
12549  */
12550     last : function(){
12551         return this.items[this.length-1];   
12552     },
12553     
12554     _sort : function(property, dir, fn){
12555         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12556         fn = fn || function(a, b){
12557             return a-b;
12558         };
12559         var c = [], k = this.keys, items = this.items;
12560         for(var i = 0, len = items.length; i < len; i++){
12561             c[c.length] = {key: k[i], value: items[i], index: i};
12562         }
12563         c.sort(function(a, b){
12564             var v = fn(a[property], b[property]) * dsc;
12565             if(v == 0){
12566                 v = (a.index < b.index ? -1 : 1);
12567             }
12568             return v;
12569         });
12570         for(var i = 0, len = c.length; i < len; i++){
12571             items[i] = c[i].value;
12572             k[i] = c[i].key;
12573         }
12574         this.fireEvent("sort", this);
12575     },
12576     
12577     /**
12578      * Sorts this collection with the passed comparison function
12579      * @param {String} direction (optional) "ASC" or "DESC"
12580      * @param {Function} fn (optional) comparison function
12581      */
12582     sort : function(dir, fn){
12583         this._sort("value", dir, fn);
12584     },
12585     
12586     /**
12587      * Sorts this collection by keys
12588      * @param {String} direction (optional) "ASC" or "DESC"
12589      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12590      */
12591     keySort : function(dir, fn){
12592         this._sort("key", dir, fn || function(a, b){
12593             return String(a).toUpperCase()-String(b).toUpperCase();
12594         });
12595     },
12596     
12597     /**
12598      * Returns a range of items in this collection
12599      * @param {Number} startIndex (optional) defaults to 0
12600      * @param {Number} endIndex (optional) default to the last item
12601      * @return {Array} An array of items
12602      */
12603     getRange : function(start, end){
12604         var items = this.items;
12605         if(items.length < 1){
12606             return [];
12607         }
12608         start = start || 0;
12609         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12610         var r = [];
12611         if(start <= end){
12612             for(var i = start; i <= end; i++) {
12613                     r[r.length] = items[i];
12614             }
12615         }else{
12616             for(var i = start; i >= end; i--) {
12617                     r[r.length] = items[i];
12618             }
12619         }
12620         return r;
12621     },
12622         
12623     /**
12624      * Filter the <i>objects</i> in this collection by a specific property. 
12625      * Returns a new collection that has been filtered.
12626      * @param {String} property A property on your objects
12627      * @param {String/RegExp} value Either string that the property values 
12628      * should start with or a RegExp to test against the property
12629      * @return {MixedCollection} The new filtered collection
12630      */
12631     filter : function(property, value){
12632         if(!value.exec){ // not a regex
12633             value = String(value);
12634             if(value.length == 0){
12635                 return this.clone();
12636             }
12637             value = new RegExp("^" + Roo.escapeRe(value), "i");
12638         }
12639         return this.filterBy(function(o){
12640             return o && value.test(o[property]);
12641         });
12642         },
12643     
12644     /**
12645      * Filter by a function. * Returns a new collection that has been filtered.
12646      * The passed function will be called with each 
12647      * object in the collection. If the function returns true, the value is included 
12648      * otherwise it is filtered.
12649      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12650      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12651      * @return {MixedCollection} The new filtered collection
12652      */
12653     filterBy : function(fn, scope){
12654         var r = new Roo.util.MixedCollection();
12655         r.getKey = this.getKey;
12656         var k = this.keys, it = this.items;
12657         for(var i = 0, len = it.length; i < len; i++){
12658             if(fn.call(scope||this, it[i], k[i])){
12659                                 r.add(k[i], it[i]);
12660                         }
12661         }
12662         return r;
12663     },
12664     
12665     /**
12666      * Creates a duplicate of this collection
12667      * @return {MixedCollection}
12668      */
12669     clone : function(){
12670         var r = new Roo.util.MixedCollection();
12671         var k = this.keys, it = this.items;
12672         for(var i = 0, len = it.length; i < len; i++){
12673             r.add(k[i], it[i]);
12674         }
12675         r.getKey = this.getKey;
12676         return r;
12677     }
12678 });
12679 /**
12680  * Returns the item associated with the passed key or index.
12681  * @method
12682  * @param {String/Number} key The key or index of the item.
12683  * @return {Object} The item associated with the passed key.
12684  */
12685 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12686  * Based on:
12687  * Ext JS Library 1.1.1
12688  * Copyright(c) 2006-2007, Ext JS, LLC.
12689  *
12690  * Originally Released Under LGPL - original licence link has changed is not relivant.
12691  *
12692  * Fork - LGPL
12693  * <script type="text/javascript">
12694  */
12695 /**
12696  * @class Roo.util.JSON
12697  * Modified version of Douglas Crockford"s json.js that doesn"t
12698  * mess with the Object prototype 
12699  * http://www.json.org/js.html
12700  * @singleton
12701  */
12702 Roo.util.JSON = new (function(){
12703     var useHasOwn = {}.hasOwnProperty ? true : false;
12704     
12705     // crashes Safari in some instances
12706     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12707     
12708     var pad = function(n) {
12709         return n < 10 ? "0" + n : n;
12710     };
12711     
12712     var m = {
12713         "\b": '\\b',
12714         "\t": '\\t',
12715         "\n": '\\n',
12716         "\f": '\\f',
12717         "\r": '\\r',
12718         '"' : '\\"',
12719         "\\": '\\\\'
12720     };
12721
12722     var encodeString = function(s){
12723         if (/["\\\x00-\x1f]/.test(s)) {
12724             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12725                 var c = m[b];
12726                 if(c){
12727                     return c;
12728                 }
12729                 c = b.charCodeAt();
12730                 return "\\u00" +
12731                     Math.floor(c / 16).toString(16) +
12732                     (c % 16).toString(16);
12733             }) + '"';
12734         }
12735         return '"' + s + '"';
12736     };
12737     
12738     var encodeArray = function(o){
12739         var a = ["["], b, i, l = o.length, v;
12740             for (i = 0; i < l; i += 1) {
12741                 v = o[i];
12742                 switch (typeof v) {
12743                     case "undefined":
12744                     case "function":
12745                     case "unknown":
12746                         break;
12747                     default:
12748                         if (b) {
12749                             a.push(',');
12750                         }
12751                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12752                         b = true;
12753                 }
12754             }
12755             a.push("]");
12756             return a.join("");
12757     };
12758     
12759     var encodeDate = function(o){
12760         return '"' + o.getFullYear() + "-" +
12761                 pad(o.getMonth() + 1) + "-" +
12762                 pad(o.getDate()) + "T" +
12763                 pad(o.getHours()) + ":" +
12764                 pad(o.getMinutes()) + ":" +
12765                 pad(o.getSeconds()) + '"';
12766     };
12767     
12768     /**
12769      * Encodes an Object, Array or other value
12770      * @param {Mixed} o The variable to encode
12771      * @return {String} The JSON string
12772      */
12773     this.encode = function(o){
12774         if(typeof o == "undefined" || o === null){
12775             return "null";
12776         }else if(o instanceof Array){
12777             return encodeArray(o);
12778         }else if(o instanceof Date){
12779             return encodeDate(o);
12780         }else if(typeof o == "string"){
12781             return encodeString(o);
12782         }else if(typeof o == "number"){
12783             return isFinite(o) ? String(o) : "null";
12784         }else if(typeof o == "boolean"){
12785             return String(o);
12786         }else {
12787             var a = ["{"], b, i, v;
12788             for (i in o) {
12789                 if(!useHasOwn || o.hasOwnProperty(i)) {
12790                     v = o[i];
12791                     switch (typeof v) {
12792                     case "undefined":
12793                     case "function":
12794                     case "unknown":
12795                         break;
12796                     default:
12797                         if(b){
12798                             a.push(',');
12799                         }
12800                         a.push(this.encode(i), ":",
12801                                 v === null ? "null" : this.encode(v));
12802                         b = true;
12803                     }
12804                 }
12805             }
12806             a.push("}");
12807             return a.join("");
12808         }
12809     };
12810     
12811     /**
12812      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12813      * @param {String} json The JSON string
12814      * @return {Object} The resulting object
12815      */
12816     this.decode = function(json){
12817         
12818         return /** eval:var:json */ eval("(" + json + ')');
12819     };
12820 })();
12821 /** 
12822  * Shorthand for {@link Roo.util.JSON#encode}
12823  * @member Roo encode 
12824  * @method */
12825 Roo.encode = Roo.util.JSON.encode;
12826 /** 
12827  * Shorthand for {@link Roo.util.JSON#decode}
12828  * @member Roo decode 
12829  * @method */
12830 Roo.decode = Roo.util.JSON.decode;
12831 /*
12832  * Based on:
12833  * Ext JS Library 1.1.1
12834  * Copyright(c) 2006-2007, Ext JS, LLC.
12835  *
12836  * Originally Released Under LGPL - original licence link has changed is not relivant.
12837  *
12838  * Fork - LGPL
12839  * <script type="text/javascript">
12840  */
12841  
12842 /**
12843  * @class Roo.util.Format
12844  * Reusable data formatting functions
12845  * @singleton
12846  */
12847 Roo.util.Format = function(){
12848     var trimRe = /^\s+|\s+$/g;
12849     return {
12850         /**
12851          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12852          * @param {String} value The string to truncate
12853          * @param {Number} length The maximum length to allow before truncating
12854          * @return {String} The converted text
12855          */
12856         ellipsis : function(value, len){
12857             if(value && value.length > len){
12858                 return value.substr(0, len-3)+"...";
12859             }
12860             return value;
12861         },
12862
12863         /**
12864          * Checks a reference and converts it to empty string if it is undefined
12865          * @param {Mixed} value Reference to check
12866          * @return {Mixed} Empty string if converted, otherwise the original value
12867          */
12868         undef : function(value){
12869             return typeof value != "undefined" ? value : "";
12870         },
12871
12872         /**
12873          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12874          * @param {String} value The string to encode
12875          * @return {String} The encoded text
12876          */
12877         htmlEncode : function(value){
12878             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12879         },
12880
12881         /**
12882          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12883          * @param {String} value The string to decode
12884          * @return {String} The decoded text
12885          */
12886         htmlDecode : function(value){
12887             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12888         },
12889
12890         /**
12891          * Trims any whitespace from either side of a string
12892          * @param {String} value The text to trim
12893          * @return {String} The trimmed text
12894          */
12895         trim : function(value){
12896             return String(value).replace(trimRe, "");
12897         },
12898
12899         /**
12900          * Returns a substring from within an original string
12901          * @param {String} value The original text
12902          * @param {Number} start The start index of the substring
12903          * @param {Number} length The length of the substring
12904          * @return {String} The substring
12905          */
12906         substr : function(value, start, length){
12907             return String(value).substr(start, length);
12908         },
12909
12910         /**
12911          * Converts a string to all lower case letters
12912          * @param {String} value The text to convert
12913          * @return {String} The converted text
12914          */
12915         lowercase : function(value){
12916             return String(value).toLowerCase();
12917         },
12918
12919         /**
12920          * Converts a string to all upper case letters
12921          * @param {String} value The text to convert
12922          * @return {String} The converted text
12923          */
12924         uppercase : function(value){
12925             return String(value).toUpperCase();
12926         },
12927
12928         /**
12929          * Converts the first character only of a string to upper case
12930          * @param {String} value The text to convert
12931          * @return {String} The converted text
12932          */
12933         capitalize : function(value){
12934             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12935         },
12936
12937         // private
12938         call : function(value, fn){
12939             if(arguments.length > 2){
12940                 var args = Array.prototype.slice.call(arguments, 2);
12941                 args.unshift(value);
12942                  
12943                 return /** eval:var:value */  eval(fn).apply(window, args);
12944             }else{
12945                 /** eval:var:value */
12946                 return /** eval:var:value */ eval(fn).call(window, value);
12947             }
12948         },
12949
12950         /**
12951          * Format a number as US currency
12952          * @param {Number/String} value The numeric value to format
12953          * @return {String} The formatted currency string
12954          */
12955         usMoney : function(v){
12956             v = (Math.round((v-0)*100))/100;
12957             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12958             v = String(v);
12959             var ps = v.split('.');
12960             var whole = ps[0];
12961             var sub = ps[1] ? '.'+ ps[1] : '.00';
12962             var r = /(\d+)(\d{3})/;
12963             while (r.test(whole)) {
12964                 whole = whole.replace(r, '$1' + ',' + '$2');
12965             }
12966             return "$" + whole + sub ;
12967         },
12968
12969         /**
12970          * Parse a value into a formatted date using the specified format pattern.
12971          * @param {Mixed} value The value to format
12972          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12973          * @return {String} The formatted date string
12974          */
12975         date : function(v, format){
12976             if(!v){
12977                 return "";
12978             }
12979             if(!(v instanceof Date)){
12980                 v = new Date(Date.parse(v));
12981             }
12982             return v.dateFormat(format || "m/d/Y");
12983         },
12984
12985         /**
12986          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12987          * @param {String} format Any valid date format string
12988          * @return {Function} The date formatting function
12989          */
12990         dateRenderer : function(format){
12991             return function(v){
12992                 return Roo.util.Format.date(v, format);  
12993             };
12994         },
12995
12996         // private
12997         stripTagsRE : /<\/?[^>]+>/gi,
12998         
12999         /**
13000          * Strips all HTML tags
13001          * @param {Mixed} value The text from which to strip tags
13002          * @return {String} The stripped text
13003          */
13004         stripTags : function(v){
13005             return !v ? v : String(v).replace(this.stripTagsRE, "");
13006         }
13007     };
13008 }();/*
13009  * Based on:
13010  * Ext JS Library 1.1.1
13011  * Copyright(c) 2006-2007, Ext JS, LLC.
13012  *
13013  * Originally Released Under LGPL - original licence link has changed is not relivant.
13014  *
13015  * Fork - LGPL
13016  * <script type="text/javascript">
13017  */
13018
13019
13020  
13021
13022 /**
13023  * @class Roo.MasterTemplate
13024  * @extends Roo.Template
13025  * Provides a template that can have child templates. The syntax is:
13026 <pre><code>
13027 var t = new Roo.MasterTemplate(
13028         '&lt;select name="{name}"&gt;',
13029                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13030         '&lt;/select&gt;'
13031 );
13032 t.add('options', {value: 'foo', text: 'bar'});
13033 // or you can add multiple child elements in one shot
13034 t.addAll('options', [
13035     {value: 'foo', text: 'bar'},
13036     {value: 'foo2', text: 'bar2'},
13037     {value: 'foo3', text: 'bar3'}
13038 ]);
13039 // then append, applying the master template values
13040 t.append('my-form', {name: 'my-select'});
13041 </code></pre>
13042 * A name attribute for the child template is not required if you have only one child
13043 * template or you want to refer to them by index.
13044  */
13045 Roo.MasterTemplate = function(){
13046     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13047     this.originalHtml = this.html;
13048     var st = {};
13049     var m, re = this.subTemplateRe;
13050     re.lastIndex = 0;
13051     var subIndex = 0;
13052     while(m = re.exec(this.html)){
13053         var name = m[1], content = m[2];
13054         st[subIndex] = {
13055             name: name,
13056             index: subIndex,
13057             buffer: [],
13058             tpl : new Roo.Template(content)
13059         };
13060         if(name){
13061             st[name] = st[subIndex];
13062         }
13063         st[subIndex].tpl.compile();
13064         st[subIndex].tpl.call = this.call.createDelegate(this);
13065         subIndex++;
13066     }
13067     this.subCount = subIndex;
13068     this.subs = st;
13069 };
13070 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13071     /**
13072     * The regular expression used to match sub templates
13073     * @type RegExp
13074     * @property
13075     */
13076     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13077
13078     /**
13079      * Applies the passed values to a child template.
13080      * @param {String/Number} name (optional) The name or index of the child template
13081      * @param {Array/Object} values The values to be applied to the template
13082      * @return {MasterTemplate} this
13083      */
13084      add : function(name, values){
13085         if(arguments.length == 1){
13086             values = arguments[0];
13087             name = 0;
13088         }
13089         var s = this.subs[name];
13090         s.buffer[s.buffer.length] = s.tpl.apply(values);
13091         return this;
13092     },
13093
13094     /**
13095      * Applies all the passed values to a child template.
13096      * @param {String/Number} name (optional) The name or index of the child template
13097      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13098      * @param {Boolean} reset (optional) True to reset the template first
13099      * @return {MasterTemplate} this
13100      */
13101     fill : function(name, values, reset){
13102         var a = arguments;
13103         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13104             values = a[0];
13105             name = 0;
13106             reset = a[1];
13107         }
13108         if(reset){
13109             this.reset();
13110         }
13111         for(var i = 0, len = values.length; i < len; i++){
13112             this.add(name, values[i]);
13113         }
13114         return this;
13115     },
13116
13117     /**
13118      * Resets the template for reuse
13119      * @return {MasterTemplate} this
13120      */
13121      reset : function(){
13122         var s = this.subs;
13123         for(var i = 0; i < this.subCount; i++){
13124             s[i].buffer = [];
13125         }
13126         return this;
13127     },
13128
13129     applyTemplate : function(values){
13130         var s = this.subs;
13131         var replaceIndex = -1;
13132         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13133             return s[++replaceIndex].buffer.join("");
13134         });
13135         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13136     },
13137
13138     apply : function(){
13139         return this.applyTemplate.apply(this, arguments);
13140     },
13141
13142     compile : function(){return this;}
13143 });
13144
13145 /**
13146  * Alias for fill().
13147  * @method
13148  */
13149 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13150  /**
13151  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13152  * var tpl = Roo.MasterTemplate.from('element-id');
13153  * @param {String/HTMLElement} el
13154  * @param {Object} config
13155  * @static
13156  */
13157 Roo.MasterTemplate.from = function(el, config){
13158     el = Roo.getDom(el);
13159     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13160 };/*
13161  * Based on:
13162  * Ext JS Library 1.1.1
13163  * Copyright(c) 2006-2007, Ext JS, LLC.
13164  *
13165  * Originally Released Under LGPL - original licence link has changed is not relivant.
13166  *
13167  * Fork - LGPL
13168  * <script type="text/javascript">
13169  */
13170
13171  
13172 /**
13173  * @class Roo.util.CSS
13174  * Utility class for manipulating CSS rules
13175  * @singleton
13176  */
13177 Roo.util.CSS = function(){
13178         var rules = null;
13179         var doc = document;
13180
13181     var camelRe = /(-[a-z])/gi;
13182     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13183
13184    return {
13185    /**
13186     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13187     * tag and appended to the HEAD of the document.
13188     * @param {String|Object} cssText The text containing the css rules
13189     * @param {String} id An id to add to the stylesheet for later removal
13190     * @return {StyleSheet}
13191     */
13192     createStyleSheet : function(cssText, id){
13193         var ss;
13194         var head = doc.getElementsByTagName("head")[0];
13195         var nrules = doc.createElement("style");
13196         nrules.setAttribute("type", "text/css");
13197         if(id){
13198             nrules.setAttribute("id", id);
13199         }
13200         if (typeof(cssText) != 'string') {
13201             // support object maps..
13202             // not sure if this a good idea.. 
13203             // perhaps it should be merged with the general css handling
13204             // and handle js style props.
13205             var cssTextNew = [];
13206             for(var n in cssText) {
13207                 var citems = [];
13208                 for(var k in cssText[n]) {
13209                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13210                 }
13211                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13212                 
13213             }
13214             cssText = cssTextNew.join("\n");
13215             
13216         }
13217        
13218        
13219        if(Roo.isIE){
13220            head.appendChild(nrules);
13221            ss = nrules.styleSheet;
13222            ss.cssText = cssText;
13223        }else{
13224            try{
13225                 nrules.appendChild(doc.createTextNode(cssText));
13226            }catch(e){
13227                nrules.cssText = cssText; 
13228            }
13229            head.appendChild(nrules);
13230            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13231        }
13232        this.cacheStyleSheet(ss);
13233        return ss;
13234    },
13235
13236    /**
13237     * Removes a style or link tag by id
13238     * @param {String} id The id of the tag
13239     */
13240    removeStyleSheet : function(id){
13241        var existing = doc.getElementById(id);
13242        if(existing){
13243            existing.parentNode.removeChild(existing);
13244        }
13245    },
13246
13247    /**
13248     * Dynamically swaps an existing stylesheet reference for a new one
13249     * @param {String} id The id of an existing link tag to remove
13250     * @param {String} url The href of the new stylesheet to include
13251     */
13252    swapStyleSheet : function(id, url){
13253        this.removeStyleSheet(id);
13254        var ss = doc.createElement("link");
13255        ss.setAttribute("rel", "stylesheet");
13256        ss.setAttribute("type", "text/css");
13257        ss.setAttribute("id", id);
13258        ss.setAttribute("href", url);
13259        doc.getElementsByTagName("head")[0].appendChild(ss);
13260    },
13261    
13262    /**
13263     * Refresh the rule cache if you have dynamically added stylesheets
13264     * @return {Object} An object (hash) of rules indexed by selector
13265     */
13266    refreshCache : function(){
13267        return this.getRules(true);
13268    },
13269
13270    // private
13271    cacheStyleSheet : function(stylesheet){
13272        if(!rules){
13273            rules = {};
13274        }
13275        try{// try catch for cross domain access issue
13276            var ssRules = stylesheet.cssRules || stylesheet.rules;
13277            for(var j = ssRules.length-1; j >= 0; --j){
13278                rules[ssRules[j].selectorText] = ssRules[j];
13279            }
13280        }catch(e){}
13281    },
13282    
13283    /**
13284     * Gets all css rules for the document
13285     * @param {Boolean} refreshCache true to refresh the internal cache
13286     * @return {Object} An object (hash) of rules indexed by selector
13287     */
13288    getRules : function(refreshCache){
13289                 if(rules == null || refreshCache){
13290                         rules = {};
13291                         var ds = doc.styleSheets;
13292                         for(var i =0, len = ds.length; i < len; i++){
13293                             try{
13294                         this.cacheStyleSheet(ds[i]);
13295                     }catch(e){} 
13296                 }
13297                 }
13298                 return rules;
13299         },
13300         
13301         /**
13302     * Gets an an individual CSS rule by selector(s)
13303     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13304     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13305     * @return {CSSRule} The CSS rule or null if one is not found
13306     */
13307    getRule : function(selector, refreshCache){
13308                 var rs = this.getRules(refreshCache);
13309                 if(!(selector instanceof Array)){
13310                     return rs[selector];
13311                 }
13312                 for(var i = 0; i < selector.length; i++){
13313                         if(rs[selector[i]]){
13314                                 return rs[selector[i]];
13315                         }
13316                 }
13317                 return null;
13318         },
13319         
13320         
13321         /**
13322     * Updates a rule property
13323     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13324     * @param {String} property The css property
13325     * @param {String} value The new value for the property
13326     * @return {Boolean} true If a rule was found and updated
13327     */
13328    updateRule : function(selector, property, value){
13329                 if(!(selector instanceof Array)){
13330                         var rule = this.getRule(selector);
13331                         if(rule){
13332                                 rule.style[property.replace(camelRe, camelFn)] = value;
13333                                 return true;
13334                         }
13335                 }else{
13336                         for(var i = 0; i < selector.length; i++){
13337                                 if(this.updateRule(selector[i], property, value)){
13338                                         return true;
13339                                 }
13340                         }
13341                 }
13342                 return false;
13343         }
13344    };   
13345 }();/*
13346  * Based on:
13347  * Ext JS Library 1.1.1
13348  * Copyright(c) 2006-2007, Ext JS, LLC.
13349  *
13350  * Originally Released Under LGPL - original licence link has changed is not relivant.
13351  *
13352  * Fork - LGPL
13353  * <script type="text/javascript">
13354  */
13355
13356  
13357
13358 /**
13359  * @class Roo.util.ClickRepeater
13360  * @extends Roo.util.Observable
13361  * 
13362  * A wrapper class which can be applied to any element. Fires a "click" event while the
13363  * mouse is pressed. The interval between firings may be specified in the config but
13364  * defaults to 10 milliseconds.
13365  * 
13366  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13367  * 
13368  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13369  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13370  * Similar to an autorepeat key delay.
13371  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13372  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13373  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13374  *           "interval" and "delay" are ignored. "immediate" is honored.
13375  * @cfg {Boolean} preventDefault True to prevent the default click event
13376  * @cfg {Boolean} stopDefault True to stop the default click event
13377  * 
13378  * @history
13379  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13380  *     2007-02-02 jvs Renamed to ClickRepeater
13381  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13382  *
13383  *  @constructor
13384  * @param {String/HTMLElement/Element} el The element to listen on
13385  * @param {Object} config
13386  **/
13387 Roo.util.ClickRepeater = function(el, config)
13388 {
13389     this.el = Roo.get(el);
13390     this.el.unselectable();
13391
13392     Roo.apply(this, config);
13393
13394     this.addEvents({
13395     /**
13396      * @event mousedown
13397      * Fires when the mouse button is depressed.
13398      * @param {Roo.util.ClickRepeater} this
13399      */
13400         "mousedown" : true,
13401     /**
13402      * @event click
13403      * Fires on a specified interval during the time the element is pressed.
13404      * @param {Roo.util.ClickRepeater} this
13405      */
13406         "click" : true,
13407     /**
13408      * @event mouseup
13409      * Fires when the mouse key is released.
13410      * @param {Roo.util.ClickRepeater} this
13411      */
13412         "mouseup" : true
13413     });
13414
13415     this.el.on("mousedown", this.handleMouseDown, this);
13416     if(this.preventDefault || this.stopDefault){
13417         this.el.on("click", function(e){
13418             if(this.preventDefault){
13419                 e.preventDefault();
13420             }
13421             if(this.stopDefault){
13422                 e.stopEvent();
13423             }
13424         }, this);
13425     }
13426
13427     // allow inline handler
13428     if(this.handler){
13429         this.on("click", this.handler,  this.scope || this);
13430     }
13431
13432     Roo.util.ClickRepeater.superclass.constructor.call(this);
13433 };
13434
13435 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13436     interval : 20,
13437     delay: 250,
13438     preventDefault : true,
13439     stopDefault : false,
13440     timer : 0,
13441
13442     // private
13443     handleMouseDown : function(){
13444         clearTimeout(this.timer);
13445         this.el.blur();
13446         if(this.pressClass){
13447             this.el.addClass(this.pressClass);
13448         }
13449         this.mousedownTime = new Date();
13450
13451         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13452         this.el.on("mouseout", this.handleMouseOut, this);
13453
13454         this.fireEvent("mousedown", this);
13455         this.fireEvent("click", this);
13456         
13457         this.timer = this.click.defer(this.delay || this.interval, this);
13458     },
13459
13460     // private
13461     click : function(){
13462         this.fireEvent("click", this);
13463         this.timer = this.click.defer(this.getInterval(), this);
13464     },
13465
13466     // private
13467     getInterval: function(){
13468         if(!this.accelerate){
13469             return this.interval;
13470         }
13471         var pressTime = this.mousedownTime.getElapsed();
13472         if(pressTime < 500){
13473             return 400;
13474         }else if(pressTime < 1700){
13475             return 320;
13476         }else if(pressTime < 2600){
13477             return 250;
13478         }else if(pressTime < 3500){
13479             return 180;
13480         }else if(pressTime < 4400){
13481             return 140;
13482         }else if(pressTime < 5300){
13483             return 80;
13484         }else if(pressTime < 6200){
13485             return 50;
13486         }else{
13487             return 10;
13488         }
13489     },
13490
13491     // private
13492     handleMouseOut : function(){
13493         clearTimeout(this.timer);
13494         if(this.pressClass){
13495             this.el.removeClass(this.pressClass);
13496         }
13497         this.el.on("mouseover", this.handleMouseReturn, this);
13498     },
13499
13500     // private
13501     handleMouseReturn : function(){
13502         this.el.un("mouseover", this.handleMouseReturn);
13503         if(this.pressClass){
13504             this.el.addClass(this.pressClass);
13505         }
13506         this.click();
13507     },
13508
13509     // private
13510     handleMouseUp : function(){
13511         clearTimeout(this.timer);
13512         this.el.un("mouseover", this.handleMouseReturn);
13513         this.el.un("mouseout", this.handleMouseOut);
13514         Roo.get(document).un("mouseup", this.handleMouseUp);
13515         this.el.removeClass(this.pressClass);
13516         this.fireEvent("mouseup", this);
13517     }
13518 });/*
13519  * Based on:
13520  * Ext JS Library 1.1.1
13521  * Copyright(c) 2006-2007, Ext JS, LLC.
13522  *
13523  * Originally Released Under LGPL - original licence link has changed is not relivant.
13524  *
13525  * Fork - LGPL
13526  * <script type="text/javascript">
13527  */
13528
13529  
13530 /**
13531  * @class Roo.KeyNav
13532  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13533  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13534  * way to implement custom navigation schemes for any UI component.</p>
13535  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13536  * pageUp, pageDown, del, home, end.  Usage:</p>
13537  <pre><code>
13538 var nav = new Roo.KeyNav("my-element", {
13539     "left" : function(e){
13540         this.moveLeft(e.ctrlKey);
13541     },
13542     "right" : function(e){
13543         this.moveRight(e.ctrlKey);
13544     },
13545     "enter" : function(e){
13546         this.save();
13547     },
13548     scope : this
13549 });
13550 </code></pre>
13551  * @constructor
13552  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13553  * @param {Object} config The config
13554  */
13555 Roo.KeyNav = function(el, config){
13556     this.el = Roo.get(el);
13557     Roo.apply(this, config);
13558     if(!this.disabled){
13559         this.disabled = true;
13560         this.enable();
13561     }
13562 };
13563
13564 Roo.KeyNav.prototype = {
13565     /**
13566      * @cfg {Boolean} disabled
13567      * True to disable this KeyNav instance (defaults to false)
13568      */
13569     disabled : false,
13570     /**
13571      * @cfg {String} defaultEventAction
13572      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13573      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13574      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13575      */
13576     defaultEventAction: "stopEvent",
13577     /**
13578      * @cfg {Boolean} forceKeyDown
13579      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13580      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13581      * handle keydown instead of keypress.
13582      */
13583     forceKeyDown : false,
13584
13585     // private
13586     prepareEvent : function(e){
13587         var k = e.getKey();
13588         var h = this.keyToHandler[k];
13589         //if(h && this[h]){
13590         //    e.stopPropagation();
13591         //}
13592         if(Roo.isSafari && h && k >= 37 && k <= 40){
13593             e.stopEvent();
13594         }
13595     },
13596
13597     // private
13598     relay : function(e){
13599         var k = e.getKey();
13600         var h = this.keyToHandler[k];
13601         if(h && this[h]){
13602             if(this.doRelay(e, this[h], h) !== true){
13603                 e[this.defaultEventAction]();
13604             }
13605         }
13606     },
13607
13608     // private
13609     doRelay : function(e, h, hname){
13610         return h.call(this.scope || this, e);
13611     },
13612
13613     // possible handlers
13614     enter : false,
13615     left : false,
13616     right : false,
13617     up : false,
13618     down : false,
13619     tab : false,
13620     esc : false,
13621     pageUp : false,
13622     pageDown : false,
13623     del : false,
13624     home : false,
13625     end : false,
13626
13627     // quick lookup hash
13628     keyToHandler : {
13629         37 : "left",
13630         39 : "right",
13631         38 : "up",
13632         40 : "down",
13633         33 : "pageUp",
13634         34 : "pageDown",
13635         46 : "del",
13636         36 : "home",
13637         35 : "end",
13638         13 : "enter",
13639         27 : "esc",
13640         9  : "tab"
13641     },
13642
13643         /**
13644          * Enable this KeyNav
13645          */
13646         enable: function(){
13647                 if(this.disabled){
13648             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13649             // the EventObject will normalize Safari automatically
13650             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13651                 this.el.on("keydown", this.relay,  this);
13652             }else{
13653                 this.el.on("keydown", this.prepareEvent,  this);
13654                 this.el.on("keypress", this.relay,  this);
13655             }
13656                     this.disabled = false;
13657                 }
13658         },
13659
13660         /**
13661          * Disable this KeyNav
13662          */
13663         disable: function(){
13664                 if(!this.disabled){
13665                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13666                 this.el.un("keydown", this.relay);
13667             }else{
13668                 this.el.un("keydown", this.prepareEvent);
13669                 this.el.un("keypress", this.relay);
13670             }
13671                     this.disabled = true;
13672                 }
13673         }
13674 };/*
13675  * Based on:
13676  * Ext JS Library 1.1.1
13677  * Copyright(c) 2006-2007, Ext JS, LLC.
13678  *
13679  * Originally Released Under LGPL - original licence link has changed is not relivant.
13680  *
13681  * Fork - LGPL
13682  * <script type="text/javascript">
13683  */
13684
13685  
13686 /**
13687  * @class Roo.KeyMap
13688  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13689  * The constructor accepts the same config object as defined by {@link #addBinding}.
13690  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13691  * combination it will call the function with this signature (if the match is a multi-key
13692  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13693  * A KeyMap can also handle a string representation of keys.<br />
13694  * Usage:
13695  <pre><code>
13696 // map one key by key code
13697 var map = new Roo.KeyMap("my-element", {
13698     key: 13, // or Roo.EventObject.ENTER
13699     fn: myHandler,
13700     scope: myObject
13701 });
13702
13703 // map multiple keys to one action by string
13704 var map = new Roo.KeyMap("my-element", {
13705     key: "a\r\n\t",
13706     fn: myHandler,
13707     scope: myObject
13708 });
13709
13710 // map multiple keys to multiple actions by strings and array of codes
13711 var map = new Roo.KeyMap("my-element", [
13712     {
13713         key: [10,13],
13714         fn: function(){ alert("Return was pressed"); }
13715     }, {
13716         key: "abc",
13717         fn: function(){ alert('a, b or c was pressed'); }
13718     }, {
13719         key: "\t",
13720         ctrl:true,
13721         shift:true,
13722         fn: function(){ alert('Control + shift + tab was pressed.'); }
13723     }
13724 ]);
13725 </code></pre>
13726  * <b>Note: A KeyMap starts enabled</b>
13727  * @constructor
13728  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13729  * @param {Object} config The config (see {@link #addBinding})
13730  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13731  */
13732 Roo.KeyMap = function(el, config, eventName){
13733     this.el  = Roo.get(el);
13734     this.eventName = eventName || "keydown";
13735     this.bindings = [];
13736     if(config){
13737         this.addBinding(config);
13738     }
13739     this.enable();
13740 };
13741
13742 Roo.KeyMap.prototype = {
13743     /**
13744      * True to stop the event from bubbling and prevent the default browser action if the
13745      * key was handled by the KeyMap (defaults to false)
13746      * @type Boolean
13747      */
13748     stopEvent : false,
13749
13750     /**
13751      * Add a new binding to this KeyMap. The following config object properties are supported:
13752      * <pre>
13753 Property    Type             Description
13754 ----------  ---------------  ----------------------------------------------------------------------
13755 key         String/Array     A single keycode or an array of keycodes to handle
13756 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13757 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13758 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13759 fn          Function         The function to call when KeyMap finds the expected key combination
13760 scope       Object           The scope of the callback function
13761 </pre>
13762      *
13763      * Usage:
13764      * <pre><code>
13765 // Create a KeyMap
13766 var map = new Roo.KeyMap(document, {
13767     key: Roo.EventObject.ENTER,
13768     fn: handleKey,
13769     scope: this
13770 });
13771
13772 //Add a new binding to the existing KeyMap later
13773 map.addBinding({
13774     key: 'abc',
13775     shift: true,
13776     fn: handleKey,
13777     scope: this
13778 });
13779 </code></pre>
13780      * @param {Object/Array} config A single KeyMap config or an array of configs
13781      */
13782         addBinding : function(config){
13783         if(config instanceof Array){
13784             for(var i = 0, len = config.length; i < len; i++){
13785                 this.addBinding(config[i]);
13786             }
13787             return;
13788         }
13789         var keyCode = config.key,
13790             shift = config.shift, 
13791             ctrl = config.ctrl, 
13792             alt = config.alt,
13793             fn = config.fn,
13794             scope = config.scope;
13795         if(typeof keyCode == "string"){
13796             var ks = [];
13797             var keyString = keyCode.toUpperCase();
13798             for(var j = 0, len = keyString.length; j < len; j++){
13799                 ks.push(keyString.charCodeAt(j));
13800             }
13801             keyCode = ks;
13802         }
13803         var keyArray = keyCode instanceof Array;
13804         var handler = function(e){
13805             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13806                 var k = e.getKey();
13807                 if(keyArray){
13808                     for(var i = 0, len = keyCode.length; i < len; i++){
13809                         if(keyCode[i] == k){
13810                           if(this.stopEvent){
13811                               e.stopEvent();
13812                           }
13813                           fn.call(scope || window, k, e);
13814                           return;
13815                         }
13816                     }
13817                 }else{
13818                     if(k == keyCode){
13819                         if(this.stopEvent){
13820                            e.stopEvent();
13821                         }
13822                         fn.call(scope || window, k, e);
13823                     }
13824                 }
13825             }
13826         };
13827         this.bindings.push(handler);  
13828         },
13829
13830     /**
13831      * Shorthand for adding a single key listener
13832      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13833      * following options:
13834      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13835      * @param {Function} fn The function to call
13836      * @param {Object} scope (optional) The scope of the function
13837      */
13838     on : function(key, fn, scope){
13839         var keyCode, shift, ctrl, alt;
13840         if(typeof key == "object" && !(key instanceof Array)){
13841             keyCode = key.key;
13842             shift = key.shift;
13843             ctrl = key.ctrl;
13844             alt = key.alt;
13845         }else{
13846             keyCode = key;
13847         }
13848         this.addBinding({
13849             key: keyCode,
13850             shift: shift,
13851             ctrl: ctrl,
13852             alt: alt,
13853             fn: fn,
13854             scope: scope
13855         })
13856     },
13857
13858     // private
13859     handleKeyDown : function(e){
13860             if(this.enabled){ //just in case
13861             var b = this.bindings;
13862             for(var i = 0, len = b.length; i < len; i++){
13863                 b[i].call(this, e);
13864             }
13865             }
13866         },
13867         
13868         /**
13869          * Returns true if this KeyMap is enabled
13870          * @return {Boolean} 
13871          */
13872         isEnabled : function(){
13873             return this.enabled;  
13874         },
13875         
13876         /**
13877          * Enables this KeyMap
13878          */
13879         enable: function(){
13880                 if(!this.enabled){
13881                     this.el.on(this.eventName, this.handleKeyDown, this);
13882                     this.enabled = true;
13883                 }
13884         },
13885
13886         /**
13887          * Disable this KeyMap
13888          */
13889         disable: function(){
13890                 if(this.enabled){
13891                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13892                     this.enabled = false;
13893                 }
13894         }
13895 };/*
13896  * Based on:
13897  * Ext JS Library 1.1.1
13898  * Copyright(c) 2006-2007, Ext JS, LLC.
13899  *
13900  * Originally Released Under LGPL - original licence link has changed is not relivant.
13901  *
13902  * Fork - LGPL
13903  * <script type="text/javascript">
13904  */
13905
13906  
13907 /**
13908  * @class Roo.util.TextMetrics
13909  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13910  * wide, in pixels, a given block of text will be.
13911  * @singleton
13912  */
13913 Roo.util.TextMetrics = function(){
13914     var shared;
13915     return {
13916         /**
13917          * Measures the size of the specified text
13918          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13919          * that can affect the size of the rendered text
13920          * @param {String} text The text to measure
13921          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13922          * in order to accurately measure the text height
13923          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13924          */
13925         measure : function(el, text, fixedWidth){
13926             if(!shared){
13927                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13928             }
13929             shared.bind(el);
13930             shared.setFixedWidth(fixedWidth || 'auto');
13931             return shared.getSize(text);
13932         },
13933
13934         /**
13935          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13936          * the overhead of multiple calls to initialize the style properties on each measurement.
13937          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13938          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13939          * in order to accurately measure the text height
13940          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13941          */
13942         createInstance : function(el, fixedWidth){
13943             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13944         }
13945     };
13946 }();
13947
13948  
13949
13950 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13951     var ml = new Roo.Element(document.createElement('div'));
13952     document.body.appendChild(ml.dom);
13953     ml.position('absolute');
13954     ml.setLeftTop(-1000, -1000);
13955     ml.hide();
13956
13957     if(fixedWidth){
13958         ml.setWidth(fixedWidth);
13959     }
13960      
13961     var instance = {
13962         /**
13963          * Returns the size of the specified text based on the internal element's style and width properties
13964          * @memberOf Roo.util.TextMetrics.Instance#
13965          * @param {String} text The text to measure
13966          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13967          */
13968         getSize : function(text){
13969             ml.update(text);
13970             var s = ml.getSize();
13971             ml.update('');
13972             return s;
13973         },
13974
13975         /**
13976          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13977          * that can affect the size of the rendered text
13978          * @memberOf Roo.util.TextMetrics.Instance#
13979          * @param {String/HTMLElement} el The element, dom node or id
13980          */
13981         bind : function(el){
13982             ml.setStyle(
13983                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13984             );
13985         },
13986
13987         /**
13988          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13989          * to set a fixed width in order to accurately measure the text height.
13990          * @memberOf Roo.util.TextMetrics.Instance#
13991          * @param {Number} width The width to set on the element
13992          */
13993         setFixedWidth : function(width){
13994             ml.setWidth(width);
13995         },
13996
13997         /**
13998          * Returns the measured width of the specified text
13999          * @memberOf Roo.util.TextMetrics.Instance#
14000          * @param {String} text The text to measure
14001          * @return {Number} width The width in pixels
14002          */
14003         getWidth : function(text){
14004             ml.dom.style.width = 'auto';
14005             return this.getSize(text).width;
14006         },
14007
14008         /**
14009          * Returns the measured height of the specified text.  For multiline text, be sure to call
14010          * {@link #setFixedWidth} if necessary.
14011          * @memberOf Roo.util.TextMetrics.Instance#
14012          * @param {String} text The text to measure
14013          * @return {Number} height The height in pixels
14014          */
14015         getHeight : function(text){
14016             return this.getSize(text).height;
14017         }
14018     };
14019
14020     instance.bind(bindTo);
14021
14022     return instance;
14023 };
14024
14025 // backwards compat
14026 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037 /**
14038  * @class Roo.state.Provider
14039  * Abstract base class for state provider implementations. This class provides methods
14040  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14041  * Provider interface.
14042  */
14043 Roo.state.Provider = function(){
14044     /**
14045      * @event statechange
14046      * Fires when a state change occurs.
14047      * @param {Provider} this This state provider
14048      * @param {String} key The state key which was changed
14049      * @param {String} value The encoded value for the state
14050      */
14051     this.addEvents({
14052         "statechange": true
14053     });
14054     this.state = {};
14055     Roo.state.Provider.superclass.constructor.call(this);
14056 };
14057 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14058     /**
14059      * Returns the current value for a key
14060      * @param {String} name The key name
14061      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14062      * @return {Mixed} The state data
14063      */
14064     get : function(name, defaultValue){
14065         return typeof this.state[name] == "undefined" ?
14066             defaultValue : this.state[name];
14067     },
14068     
14069     /**
14070      * Clears a value from the state
14071      * @param {String} name The key name
14072      */
14073     clear : function(name){
14074         delete this.state[name];
14075         this.fireEvent("statechange", this, name, null);
14076     },
14077     
14078     /**
14079      * Sets the value for a key
14080      * @param {String} name The key name
14081      * @param {Mixed} value The value to set
14082      */
14083     set : function(name, value){
14084         this.state[name] = value;
14085         this.fireEvent("statechange", this, name, value);
14086     },
14087     
14088     /**
14089      * Decodes a string previously encoded with {@link #encodeValue}.
14090      * @param {String} value The value to decode
14091      * @return {Mixed} The decoded value
14092      */
14093     decodeValue : function(cookie){
14094         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14095         var matches = re.exec(unescape(cookie));
14096         if(!matches || !matches[1]) return; // non state cookie
14097         var type = matches[1];
14098         var v = matches[2];
14099         switch(type){
14100             case "n":
14101                 return parseFloat(v);
14102             case "d":
14103                 return new Date(Date.parse(v));
14104             case "b":
14105                 return (v == "1");
14106             case "a":
14107                 var all = [];
14108                 var values = v.split("^");
14109                 for(var i = 0, len = values.length; i < len; i++){
14110                     all.push(this.decodeValue(values[i]));
14111                 }
14112                 return all;
14113            case "o":
14114                 var all = {};
14115                 var values = v.split("^");
14116                 for(var i = 0, len = values.length; i < len; i++){
14117                     var kv = values[i].split("=");
14118                     all[kv[0]] = this.decodeValue(kv[1]);
14119                 }
14120                 return all;
14121            default:
14122                 return v;
14123         }
14124     },
14125     
14126     /**
14127      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14128      * @param {Mixed} value The value to encode
14129      * @return {String} The encoded value
14130      */
14131     encodeValue : function(v){
14132         var enc;
14133         if(typeof v == "number"){
14134             enc = "n:" + v;
14135         }else if(typeof v == "boolean"){
14136             enc = "b:" + (v ? "1" : "0");
14137         }else if(v instanceof Date){
14138             enc = "d:" + v.toGMTString();
14139         }else if(v instanceof Array){
14140             var flat = "";
14141             for(var i = 0, len = v.length; i < len; i++){
14142                 flat += this.encodeValue(v[i]);
14143                 if(i != len-1) flat += "^";
14144             }
14145             enc = "a:" + flat;
14146         }else if(typeof v == "object"){
14147             var flat = "";
14148             for(var key in v){
14149                 if(typeof v[key] != "function"){
14150                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14151                 }
14152             }
14153             enc = "o:" + flat.substring(0, flat.length-1);
14154         }else{
14155             enc = "s:" + v;
14156         }
14157         return escape(enc);        
14158     }
14159 });
14160
14161 /*
14162  * Based on:
14163  * Ext JS Library 1.1.1
14164  * Copyright(c) 2006-2007, Ext JS, LLC.
14165  *
14166  * Originally Released Under LGPL - original licence link has changed is not relivant.
14167  *
14168  * Fork - LGPL
14169  * <script type="text/javascript">
14170  */
14171 /**
14172  * @class Roo.state.Manager
14173  * This is the global state manager. By default all components that are "state aware" check this class
14174  * for state information if you don't pass them a custom state provider. In order for this class
14175  * to be useful, it must be initialized with a provider when your application initializes.
14176  <pre><code>
14177 // in your initialization function
14178 init : function(){
14179    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14180    ...
14181    // supposed you have a {@link Roo.BorderLayout}
14182    var layout = new Roo.BorderLayout(...);
14183    layout.restoreState();
14184    // or a {Roo.BasicDialog}
14185    var dialog = new Roo.BasicDialog(...);
14186    dialog.restoreState();
14187  </code></pre>
14188  * @singleton
14189  */
14190 Roo.state.Manager = function(){
14191     var provider = new Roo.state.Provider();
14192     
14193     return {
14194         /**
14195          * Configures the default state provider for your application
14196          * @param {Provider} stateProvider The state provider to set
14197          */
14198         setProvider : function(stateProvider){
14199             provider = stateProvider;
14200         },
14201         
14202         /**
14203          * Returns the current value for a key
14204          * @param {String} name The key name
14205          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14206          * @return {Mixed} The state data
14207          */
14208         get : function(key, defaultValue){
14209             return provider.get(key, defaultValue);
14210         },
14211         
14212         /**
14213          * Sets the value for a key
14214          * @param {String} name The key name
14215          * @param {Mixed} value The state data
14216          */
14217          set : function(key, value){
14218             provider.set(key, value);
14219         },
14220         
14221         /**
14222          * Clears a value from the state
14223          * @param {String} name The key name
14224          */
14225         clear : function(key){
14226             provider.clear(key);
14227         },
14228         
14229         /**
14230          * Gets the currently configured state provider
14231          * @return {Provider} The state provider
14232          */
14233         getProvider : function(){
14234             return provider;
14235         }
14236     };
14237 }();
14238 /*
14239  * Based on:
14240  * Ext JS Library 1.1.1
14241  * Copyright(c) 2006-2007, Ext JS, LLC.
14242  *
14243  * Originally Released Under LGPL - original licence link has changed is not relivant.
14244  *
14245  * Fork - LGPL
14246  * <script type="text/javascript">
14247  */
14248 /**
14249  * @class Roo.state.CookieProvider
14250  * @extends Roo.state.Provider
14251  * The default Provider implementation which saves state via cookies.
14252  * <br />Usage:
14253  <pre><code>
14254    var cp = new Roo.state.CookieProvider({
14255        path: "/cgi-bin/",
14256        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14257        domain: "roojs.com"
14258    })
14259    Roo.state.Manager.setProvider(cp);
14260  </code></pre>
14261  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14262  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14263  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14264  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14265  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14266  * domain the page is running on including the 'www' like 'www.roojs.com')
14267  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14268  * @constructor
14269  * Create a new CookieProvider
14270  * @param {Object} config The configuration object
14271  */
14272 Roo.state.CookieProvider = function(config){
14273     Roo.state.CookieProvider.superclass.constructor.call(this);
14274     this.path = "/";
14275     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14276     this.domain = null;
14277     this.secure = false;
14278     Roo.apply(this, config);
14279     this.state = this.readCookies();
14280 };
14281
14282 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14283     // private
14284     set : function(name, value){
14285         if(typeof value == "undefined" || value === null){
14286             this.clear(name);
14287             return;
14288         }
14289         this.setCookie(name, value);
14290         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14291     },
14292
14293     // private
14294     clear : function(name){
14295         this.clearCookie(name);
14296         Roo.state.CookieProvider.superclass.clear.call(this, name);
14297     },
14298
14299     // private
14300     readCookies : function(){
14301         var cookies = {};
14302         var c = document.cookie + ";";
14303         var re = /\s?(.*?)=(.*?);/g;
14304         var matches;
14305         while((matches = re.exec(c)) != null){
14306             var name = matches[1];
14307             var value = matches[2];
14308             if(name && name.substring(0,3) == "ys-"){
14309                 cookies[name.substr(3)] = this.decodeValue(value);
14310             }
14311         }
14312         return cookies;
14313     },
14314
14315     // private
14316     setCookie : function(name, value){
14317         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14318            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14319            ((this.path == null) ? "" : ("; path=" + this.path)) +
14320            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14321            ((this.secure == true) ? "; secure" : "");
14322     },
14323
14324     // private
14325     clearCookie : function(name){
14326         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14327            ((this.path == null) ? "" : ("; path=" + this.path)) +
14328            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14329            ((this.secure == true) ? "; secure" : "");
14330     }
14331 });/*
14332  * Based on:
14333  * Ext JS Library 1.1.1
14334  * Copyright(c) 2006-2007, Ext JS, LLC.
14335  *
14336  * Originally Released Under LGPL - original licence link has changed is not relivant.
14337  *
14338  * Fork - LGPL
14339  * <script type="text/javascript">
14340  */
14341
14342
14343
14344 /*
14345  * These classes are derivatives of the similarly named classes in the YUI Library.
14346  * The original license:
14347  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14348  * Code licensed under the BSD License:
14349  * http://developer.yahoo.net/yui/license.txt
14350  */
14351
14352 (function() {
14353
14354 var Event=Roo.EventManager;
14355 var Dom=Roo.lib.Dom;
14356
14357 /**
14358  * @class Roo.dd.DragDrop
14359  * Defines the interface and base operation of items that that can be
14360  * dragged or can be drop targets.  It was designed to be extended, overriding
14361  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14362  * Up to three html elements can be associated with a DragDrop instance:
14363  * <ul>
14364  * <li>linked element: the element that is passed into the constructor.
14365  * This is the element which defines the boundaries for interaction with
14366  * other DragDrop objects.</li>
14367  * <li>handle element(s): The drag operation only occurs if the element that
14368  * was clicked matches a handle element.  By default this is the linked
14369  * element, but there are times that you will want only a portion of the
14370  * linked element to initiate the drag operation, and the setHandleElId()
14371  * method provides a way to define this.</li>
14372  * <li>drag element: this represents the element that would be moved along
14373  * with the cursor during a drag operation.  By default, this is the linked
14374  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14375  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14376  * </li>
14377  * </ul>
14378  * This class should not be instantiated until the onload event to ensure that
14379  * the associated elements are available.
14380  * The following would define a DragDrop obj that would interact with any
14381  * other DragDrop obj in the "group1" group:
14382  * <pre>
14383  *  dd = new Roo.dd.DragDrop("div1", "group1");
14384  * </pre>
14385  * Since none of the event handlers have been implemented, nothing would
14386  * actually happen if you were to run the code above.  Normally you would
14387  * override this class or one of the default implementations, but you can
14388  * also override the methods you want on an instance of the class...
14389  * <pre>
14390  *  dd.onDragDrop = function(e, id) {
14391  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14392  *  }
14393  * </pre>
14394  * @constructor
14395  * @param {String} id of the element that is linked to this instance
14396  * @param {String} sGroup the group of related DragDrop objects
14397  * @param {object} config an object containing configurable attributes
14398  *                Valid properties for DragDrop:
14399  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14400  */
14401 Roo.dd.DragDrop = function(id, sGroup, config) {
14402     if (id) {
14403         this.init(id, sGroup, config);
14404     }
14405 };
14406
14407 Roo.dd.DragDrop.prototype = {
14408
14409     /**
14410      * The id of the element associated with this object.  This is what we
14411      * refer to as the "linked element" because the size and position of
14412      * this element is used to determine when the drag and drop objects have
14413      * interacted.
14414      * @property id
14415      * @type String
14416      */
14417     id: null,
14418
14419     /**
14420      * Configuration attributes passed into the constructor
14421      * @property config
14422      * @type object
14423      */
14424     config: null,
14425
14426     /**
14427      * The id of the element that will be dragged.  By default this is same
14428      * as the linked element , but could be changed to another element. Ex:
14429      * Roo.dd.DDProxy
14430      * @property dragElId
14431      * @type String
14432      * @private
14433      */
14434     dragElId: null,
14435
14436     /**
14437      * the id of the element that initiates the drag operation.  By default
14438      * this is the linked element, but could be changed to be a child of this
14439      * element.  This lets us do things like only starting the drag when the
14440      * header element within the linked html element is clicked.
14441      * @property handleElId
14442      * @type String
14443      * @private
14444      */
14445     handleElId: null,
14446
14447     /**
14448      * An associative array of HTML tags that will be ignored if clicked.
14449      * @property invalidHandleTypes
14450      * @type {string: string}
14451      */
14452     invalidHandleTypes: null,
14453
14454     /**
14455      * An associative array of ids for elements that will be ignored if clicked
14456      * @property invalidHandleIds
14457      * @type {string: string}
14458      */
14459     invalidHandleIds: null,
14460
14461     /**
14462      * An indexted array of css class names for elements that will be ignored
14463      * if clicked.
14464      * @property invalidHandleClasses
14465      * @type string[]
14466      */
14467     invalidHandleClasses: null,
14468
14469     /**
14470      * The linked element's absolute X position at the time the drag was
14471      * started
14472      * @property startPageX
14473      * @type int
14474      * @private
14475      */
14476     startPageX: 0,
14477
14478     /**
14479      * The linked element's absolute X position at the time the drag was
14480      * started
14481      * @property startPageY
14482      * @type int
14483      * @private
14484      */
14485     startPageY: 0,
14486
14487     /**
14488      * The group defines a logical collection of DragDrop objects that are
14489      * related.  Instances only get events when interacting with other
14490      * DragDrop object in the same group.  This lets us define multiple
14491      * groups using a single DragDrop subclass if we want.
14492      * @property groups
14493      * @type {string: string}
14494      */
14495     groups: null,
14496
14497     /**
14498      * Individual drag/drop instances can be locked.  This will prevent
14499      * onmousedown start drag.
14500      * @property locked
14501      * @type boolean
14502      * @private
14503      */
14504     locked: false,
14505
14506     /**
14507      * Lock this instance
14508      * @method lock
14509      */
14510     lock: function() { this.locked = true; },
14511
14512     /**
14513      * Unlock this instace
14514      * @method unlock
14515      */
14516     unlock: function() { this.locked = false; },
14517
14518     /**
14519      * By default, all insances can be a drop target.  This can be disabled by
14520      * setting isTarget to false.
14521      * @method isTarget
14522      * @type boolean
14523      */
14524     isTarget: true,
14525
14526     /**
14527      * The padding configured for this drag and drop object for calculating
14528      * the drop zone intersection with this object.
14529      * @method padding
14530      * @type int[]
14531      */
14532     padding: null,
14533
14534     /**
14535      * Cached reference to the linked element
14536      * @property _domRef
14537      * @private
14538      */
14539     _domRef: null,
14540
14541     /**
14542      * Internal typeof flag
14543      * @property __ygDragDrop
14544      * @private
14545      */
14546     __ygDragDrop: true,
14547
14548     /**
14549      * Set to true when horizontal contraints are applied
14550      * @property constrainX
14551      * @type boolean
14552      * @private
14553      */
14554     constrainX: false,
14555
14556     /**
14557      * Set to true when vertical contraints are applied
14558      * @property constrainY
14559      * @type boolean
14560      * @private
14561      */
14562     constrainY: false,
14563
14564     /**
14565      * The left constraint
14566      * @property minX
14567      * @type int
14568      * @private
14569      */
14570     minX: 0,
14571
14572     /**
14573      * The right constraint
14574      * @property maxX
14575      * @type int
14576      * @private
14577      */
14578     maxX: 0,
14579
14580     /**
14581      * The up constraint
14582      * @property minY
14583      * @type int
14584      * @type int
14585      * @private
14586      */
14587     minY: 0,
14588
14589     /**
14590      * The down constraint
14591      * @property maxY
14592      * @type int
14593      * @private
14594      */
14595     maxY: 0,
14596
14597     /**
14598      * Maintain offsets when we resetconstraints.  Set to true when you want
14599      * the position of the element relative to its parent to stay the same
14600      * when the page changes
14601      *
14602      * @property maintainOffset
14603      * @type boolean
14604      */
14605     maintainOffset: false,
14606
14607     /**
14608      * Array of pixel locations the element will snap to if we specified a
14609      * horizontal graduation/interval.  This array is generated automatically
14610      * when you define a tick interval.
14611      * @property xTicks
14612      * @type int[]
14613      */
14614     xTicks: null,
14615
14616     /**
14617      * Array of pixel locations the element will snap to if we specified a
14618      * vertical graduation/interval.  This array is generated automatically
14619      * when you define a tick interval.
14620      * @property yTicks
14621      * @type int[]
14622      */
14623     yTicks: null,
14624
14625     /**
14626      * By default the drag and drop instance will only respond to the primary
14627      * button click (left button for a right-handed mouse).  Set to true to
14628      * allow drag and drop to start with any mouse click that is propogated
14629      * by the browser
14630      * @property primaryButtonOnly
14631      * @type boolean
14632      */
14633     primaryButtonOnly: true,
14634
14635     /**
14636      * The availabe property is false until the linked dom element is accessible.
14637      * @property available
14638      * @type boolean
14639      */
14640     available: false,
14641
14642     /**
14643      * By default, drags can only be initiated if the mousedown occurs in the
14644      * region the linked element is.  This is done in part to work around a
14645      * bug in some browsers that mis-report the mousedown if the previous
14646      * mouseup happened outside of the window.  This property is set to true
14647      * if outer handles are defined.
14648      *
14649      * @property hasOuterHandles
14650      * @type boolean
14651      * @default false
14652      */
14653     hasOuterHandles: false,
14654
14655     /**
14656      * Code that executes immediately before the startDrag event
14657      * @method b4StartDrag
14658      * @private
14659      */
14660     b4StartDrag: function(x, y) { },
14661
14662     /**
14663      * Abstract method called after a drag/drop object is clicked
14664      * and the drag or mousedown time thresholds have beeen met.
14665      * @method startDrag
14666      * @param {int} X click location
14667      * @param {int} Y click location
14668      */
14669     startDrag: function(x, y) { /* override this */ },
14670
14671     /**
14672      * Code that executes immediately before the onDrag event
14673      * @method b4Drag
14674      * @private
14675      */
14676     b4Drag: function(e) { },
14677
14678     /**
14679      * Abstract method called during the onMouseMove event while dragging an
14680      * object.
14681      * @method onDrag
14682      * @param {Event} e the mousemove event
14683      */
14684     onDrag: function(e) { /* override this */ },
14685
14686     /**
14687      * Abstract method called when this element fist begins hovering over
14688      * another DragDrop obj
14689      * @method onDragEnter
14690      * @param {Event} e the mousemove event
14691      * @param {String|DragDrop[]} id In POINT mode, the element
14692      * id this is hovering over.  In INTERSECT mode, an array of one or more
14693      * dragdrop items being hovered over.
14694      */
14695     onDragEnter: function(e, id) { /* override this */ },
14696
14697     /**
14698      * Code that executes immediately before the onDragOver event
14699      * @method b4DragOver
14700      * @private
14701      */
14702     b4DragOver: function(e) { },
14703
14704     /**
14705      * Abstract method called when this element is hovering over another
14706      * DragDrop obj
14707      * @method onDragOver
14708      * @param {Event} e the mousemove event
14709      * @param {String|DragDrop[]} id In POINT mode, the element
14710      * id this is hovering over.  In INTERSECT mode, an array of dd items
14711      * being hovered over.
14712      */
14713     onDragOver: function(e, id) { /* override this */ },
14714
14715     /**
14716      * Code that executes immediately before the onDragOut event
14717      * @method b4DragOut
14718      * @private
14719      */
14720     b4DragOut: function(e) { },
14721
14722     /**
14723      * Abstract method called when we are no longer hovering over an element
14724      * @method onDragOut
14725      * @param {Event} e the mousemove event
14726      * @param {String|DragDrop[]} id In POINT mode, the element
14727      * id this was hovering over.  In INTERSECT mode, an array of dd items
14728      * that the mouse is no longer over.
14729      */
14730     onDragOut: function(e, id) { /* override this */ },
14731
14732     /**
14733      * Code that executes immediately before the onDragDrop event
14734      * @method b4DragDrop
14735      * @private
14736      */
14737     b4DragDrop: function(e) { },
14738
14739     /**
14740      * Abstract method called when this item is dropped on another DragDrop
14741      * obj
14742      * @method onDragDrop
14743      * @param {Event} e the mouseup event
14744      * @param {String|DragDrop[]} id In POINT mode, the element
14745      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14746      * was dropped on.
14747      */
14748     onDragDrop: function(e, id) { /* override this */ },
14749
14750     /**
14751      * Abstract method called when this item is dropped on an area with no
14752      * drop target
14753      * @method onInvalidDrop
14754      * @param {Event} e the mouseup event
14755      */
14756     onInvalidDrop: function(e) { /* override this */ },
14757
14758     /**
14759      * Code that executes immediately before the endDrag event
14760      * @method b4EndDrag
14761      * @private
14762      */
14763     b4EndDrag: function(e) { },
14764
14765     /**
14766      * Fired when we are done dragging the object
14767      * @method endDrag
14768      * @param {Event} e the mouseup event
14769      */
14770     endDrag: function(e) { /* override this */ },
14771
14772     /**
14773      * Code executed immediately before the onMouseDown event
14774      * @method b4MouseDown
14775      * @param {Event} e the mousedown event
14776      * @private
14777      */
14778     b4MouseDown: function(e) {  },
14779
14780     /**
14781      * Event handler that fires when a drag/drop obj gets a mousedown
14782      * @method onMouseDown
14783      * @param {Event} e the mousedown event
14784      */
14785     onMouseDown: function(e) { /* override this */ },
14786
14787     /**
14788      * Event handler that fires when a drag/drop obj gets a mouseup
14789      * @method onMouseUp
14790      * @param {Event} e the mouseup event
14791      */
14792     onMouseUp: function(e) { /* override this */ },
14793
14794     /**
14795      * Override the onAvailable method to do what is needed after the initial
14796      * position was determined.
14797      * @method onAvailable
14798      */
14799     onAvailable: function () {
14800     },
14801
14802     /*
14803      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14804      * @type Object
14805      */
14806     defaultPadding : {left:0, right:0, top:0, bottom:0},
14807
14808     /*
14809      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14810  *
14811  * Usage:
14812  <pre><code>
14813  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14814                 { dragElId: "existingProxyDiv" });
14815  dd.startDrag = function(){
14816      this.constrainTo("parent-id");
14817  };
14818  </code></pre>
14819  * Or you can initalize it using the {@link Roo.Element} object:
14820  <pre><code>
14821  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14822      startDrag : function(){
14823          this.constrainTo("parent-id");
14824      }
14825  });
14826  </code></pre>
14827      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14828      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14829      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14830      * an object containing the sides to pad. For example: {right:10, bottom:10}
14831      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14832      */
14833     constrainTo : function(constrainTo, pad, inContent){
14834         if(typeof pad == "number"){
14835             pad = {left: pad, right:pad, top:pad, bottom:pad};
14836         }
14837         pad = pad || this.defaultPadding;
14838         var b = Roo.get(this.getEl()).getBox();
14839         var ce = Roo.get(constrainTo);
14840         var s = ce.getScroll();
14841         var c, cd = ce.dom;
14842         if(cd == document.body){
14843             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14844         }else{
14845             xy = ce.getXY();
14846             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14847         }
14848
14849
14850         var topSpace = b.y - c.y;
14851         var leftSpace = b.x - c.x;
14852
14853         this.resetConstraints();
14854         this.setXConstraint(leftSpace - (pad.left||0), // left
14855                 c.width - leftSpace - b.width - (pad.right||0) //right
14856         );
14857         this.setYConstraint(topSpace - (pad.top||0), //top
14858                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14859         );
14860     },
14861
14862     /**
14863      * Returns a reference to the linked element
14864      * @method getEl
14865      * @return {HTMLElement} the html element
14866      */
14867     getEl: function() {
14868         if (!this._domRef) {
14869             this._domRef = Roo.getDom(this.id);
14870         }
14871
14872         return this._domRef;
14873     },
14874
14875     /**
14876      * Returns a reference to the actual element to drag.  By default this is
14877      * the same as the html element, but it can be assigned to another
14878      * element. An example of this can be found in Roo.dd.DDProxy
14879      * @method getDragEl
14880      * @return {HTMLElement} the html element
14881      */
14882     getDragEl: function() {
14883         return Roo.getDom(this.dragElId);
14884     },
14885
14886     /**
14887      * Sets up the DragDrop object.  Must be called in the constructor of any
14888      * Roo.dd.DragDrop subclass
14889      * @method init
14890      * @param id the id of the linked element
14891      * @param {String} sGroup the group of related items
14892      * @param {object} config configuration attributes
14893      */
14894     init: function(id, sGroup, config) {
14895         this.initTarget(id, sGroup, config);
14896         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14897         // Event.on(this.id, "selectstart", Event.preventDefault);
14898     },
14899
14900     /**
14901      * Initializes Targeting functionality only... the object does not
14902      * get a mousedown handler.
14903      * @method initTarget
14904      * @param id the id of the linked element
14905      * @param {String} sGroup the group of related items
14906      * @param {object} config configuration attributes
14907      */
14908     initTarget: function(id, sGroup, config) {
14909
14910         // configuration attributes
14911         this.config = config || {};
14912
14913         // create a local reference to the drag and drop manager
14914         this.DDM = Roo.dd.DDM;
14915         // initialize the groups array
14916         this.groups = {};
14917
14918         // assume that we have an element reference instead of an id if the
14919         // parameter is not a string
14920         if (typeof id !== "string") {
14921             id = Roo.id(id);
14922         }
14923
14924         // set the id
14925         this.id = id;
14926
14927         // add to an interaction group
14928         this.addToGroup((sGroup) ? sGroup : "default");
14929
14930         // We don't want to register this as the handle with the manager
14931         // so we just set the id rather than calling the setter.
14932         this.handleElId = id;
14933
14934         // the linked element is the element that gets dragged by default
14935         this.setDragElId(id);
14936
14937         // by default, clicked anchors will not start drag operations.
14938         this.invalidHandleTypes = { A: "A" };
14939         this.invalidHandleIds = {};
14940         this.invalidHandleClasses = [];
14941
14942         this.applyConfig();
14943
14944         this.handleOnAvailable();
14945     },
14946
14947     /**
14948      * Applies the configuration parameters that were passed into the constructor.
14949      * This is supposed to happen at each level through the inheritance chain.  So
14950      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14951      * DragDrop in order to get all of the parameters that are available in
14952      * each object.
14953      * @method applyConfig
14954      */
14955     applyConfig: function() {
14956
14957         // configurable properties:
14958         //    padding, isTarget, maintainOffset, primaryButtonOnly
14959         this.padding           = this.config.padding || [0, 0, 0, 0];
14960         this.isTarget          = (this.config.isTarget !== false);
14961         this.maintainOffset    = (this.config.maintainOffset);
14962         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14963
14964     },
14965
14966     /**
14967      * Executed when the linked element is available
14968      * @method handleOnAvailable
14969      * @private
14970      */
14971     handleOnAvailable: function() {
14972         this.available = true;
14973         this.resetConstraints();
14974         this.onAvailable();
14975     },
14976
14977      /**
14978      * Configures the padding for the target zone in px.  Effectively expands
14979      * (or reduces) the virtual object size for targeting calculations.
14980      * Supports css-style shorthand; if only one parameter is passed, all sides
14981      * will have that padding, and if only two are passed, the top and bottom
14982      * will have the first param, the left and right the second.
14983      * @method setPadding
14984      * @param {int} iTop    Top pad
14985      * @param {int} iRight  Right pad
14986      * @param {int} iBot    Bot pad
14987      * @param {int} iLeft   Left pad
14988      */
14989     setPadding: function(iTop, iRight, iBot, iLeft) {
14990         // this.padding = [iLeft, iRight, iTop, iBot];
14991         if (!iRight && 0 !== iRight) {
14992             this.padding = [iTop, iTop, iTop, iTop];
14993         } else if (!iBot && 0 !== iBot) {
14994             this.padding = [iTop, iRight, iTop, iRight];
14995         } else {
14996             this.padding = [iTop, iRight, iBot, iLeft];
14997         }
14998     },
14999
15000     /**
15001      * Stores the initial placement of the linked element.
15002      * @method setInitialPosition
15003      * @param {int} diffX   the X offset, default 0
15004      * @param {int} diffY   the Y offset, default 0
15005      */
15006     setInitPosition: function(diffX, diffY) {
15007         var el = this.getEl();
15008
15009         if (!this.DDM.verifyEl(el)) {
15010             return;
15011         }
15012
15013         var dx = diffX || 0;
15014         var dy = diffY || 0;
15015
15016         var p = Dom.getXY( el );
15017
15018         this.initPageX = p[0] - dx;
15019         this.initPageY = p[1] - dy;
15020
15021         this.lastPageX = p[0];
15022         this.lastPageY = p[1];
15023
15024
15025         this.setStartPosition(p);
15026     },
15027
15028     /**
15029      * Sets the start position of the element.  This is set when the obj
15030      * is initialized, the reset when a drag is started.
15031      * @method setStartPosition
15032      * @param pos current position (from previous lookup)
15033      * @private
15034      */
15035     setStartPosition: function(pos) {
15036         var p = pos || Dom.getXY( this.getEl() );
15037         this.deltaSetXY = null;
15038
15039         this.startPageX = p[0];
15040         this.startPageY = p[1];
15041     },
15042
15043     /**
15044      * Add this instance to a group of related drag/drop objects.  All
15045      * instances belong to at least one group, and can belong to as many
15046      * groups as needed.
15047      * @method addToGroup
15048      * @param sGroup {string} the name of the group
15049      */
15050     addToGroup: function(sGroup) {
15051         this.groups[sGroup] = true;
15052         this.DDM.regDragDrop(this, sGroup);
15053     },
15054
15055     /**
15056      * Remove's this instance from the supplied interaction group
15057      * @method removeFromGroup
15058      * @param {string}  sGroup  The group to drop
15059      */
15060     removeFromGroup: function(sGroup) {
15061         if (this.groups[sGroup]) {
15062             delete this.groups[sGroup];
15063         }
15064
15065         this.DDM.removeDDFromGroup(this, sGroup);
15066     },
15067
15068     /**
15069      * Allows you to specify that an element other than the linked element
15070      * will be moved with the cursor during a drag
15071      * @method setDragElId
15072      * @param id {string} the id of the element that will be used to initiate the drag
15073      */
15074     setDragElId: function(id) {
15075         this.dragElId = id;
15076     },
15077
15078     /**
15079      * Allows you to specify a child of the linked element that should be
15080      * used to initiate the drag operation.  An example of this would be if
15081      * you have a content div with text and links.  Clicking anywhere in the
15082      * content area would normally start the drag operation.  Use this method
15083      * to specify that an element inside of the content div is the element
15084      * that starts the drag operation.
15085      * @method setHandleElId
15086      * @param id {string} the id of the element that will be used to
15087      * initiate the drag.
15088      */
15089     setHandleElId: function(id) {
15090         if (typeof id !== "string") {
15091             id = Roo.id(id);
15092         }
15093         this.handleElId = id;
15094         this.DDM.regHandle(this.id, id);
15095     },
15096
15097     /**
15098      * Allows you to set an element outside of the linked element as a drag
15099      * handle
15100      * @method setOuterHandleElId
15101      * @param id the id of the element that will be used to initiate the drag
15102      */
15103     setOuterHandleElId: function(id) {
15104         if (typeof id !== "string") {
15105             id = Roo.id(id);
15106         }
15107         Event.on(id, "mousedown",
15108                 this.handleMouseDown, this);
15109         this.setHandleElId(id);
15110
15111         this.hasOuterHandles = true;
15112     },
15113
15114     /**
15115      * Remove all drag and drop hooks for this element
15116      * @method unreg
15117      */
15118     unreg: function() {
15119         Event.un(this.id, "mousedown",
15120                 this.handleMouseDown);
15121         this._domRef = null;
15122         this.DDM._remove(this);
15123     },
15124
15125     destroy : function(){
15126         this.unreg();
15127     },
15128
15129     /**
15130      * Returns true if this instance is locked, or the drag drop mgr is locked
15131      * (meaning that all drag/drop is disabled on the page.)
15132      * @method isLocked
15133      * @return {boolean} true if this obj or all drag/drop is locked, else
15134      * false
15135      */
15136     isLocked: function() {
15137         return (this.DDM.isLocked() || this.locked);
15138     },
15139
15140     /**
15141      * Fired when this object is clicked
15142      * @method handleMouseDown
15143      * @param {Event} e
15144      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15145      * @private
15146      */
15147     handleMouseDown: function(e, oDD){
15148         if (this.primaryButtonOnly && e.button != 0) {
15149             return;
15150         }
15151
15152         if (this.isLocked()) {
15153             return;
15154         }
15155
15156         this.DDM.refreshCache(this.groups);
15157
15158         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15159         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15160         } else {
15161             if (this.clickValidator(e)) {
15162
15163                 // set the initial element position
15164                 this.setStartPosition();
15165
15166
15167                 this.b4MouseDown(e);
15168                 this.onMouseDown(e);
15169
15170                 this.DDM.handleMouseDown(e, this);
15171
15172                 this.DDM.stopEvent(e);
15173             } else {
15174
15175
15176             }
15177         }
15178     },
15179
15180     clickValidator: function(e) {
15181         var target = e.getTarget();
15182         return ( this.isValidHandleChild(target) &&
15183                     (this.id == this.handleElId ||
15184                         this.DDM.handleWasClicked(target, this.id)) );
15185     },
15186
15187     /**
15188      * Allows you to specify a tag name that should not start a drag operation
15189      * when clicked.  This is designed to facilitate embedding links within a
15190      * drag handle that do something other than start the drag.
15191      * @method addInvalidHandleType
15192      * @param {string} tagName the type of element to exclude
15193      */
15194     addInvalidHandleType: function(tagName) {
15195         var type = tagName.toUpperCase();
15196         this.invalidHandleTypes[type] = type;
15197     },
15198
15199     /**
15200      * Lets you to specify an element id for a child of a drag handle
15201      * that should not initiate a drag
15202      * @method addInvalidHandleId
15203      * @param {string} id the element id of the element you wish to ignore
15204      */
15205     addInvalidHandleId: function(id) {
15206         if (typeof id !== "string") {
15207             id = Roo.id(id);
15208         }
15209         this.invalidHandleIds[id] = id;
15210     },
15211
15212     /**
15213      * Lets you specify a css class of elements that will not initiate a drag
15214      * @method addInvalidHandleClass
15215      * @param {string} cssClass the class of the elements you wish to ignore
15216      */
15217     addInvalidHandleClass: function(cssClass) {
15218         this.invalidHandleClasses.push(cssClass);
15219     },
15220
15221     /**
15222      * Unsets an excluded tag name set by addInvalidHandleType
15223      * @method removeInvalidHandleType
15224      * @param {string} tagName the type of element to unexclude
15225      */
15226     removeInvalidHandleType: function(tagName) {
15227         var type = tagName.toUpperCase();
15228         // this.invalidHandleTypes[type] = null;
15229         delete this.invalidHandleTypes[type];
15230     },
15231
15232     /**
15233      * Unsets an invalid handle id
15234      * @method removeInvalidHandleId
15235      * @param {string} id the id of the element to re-enable
15236      */
15237     removeInvalidHandleId: function(id) {
15238         if (typeof id !== "string") {
15239             id = Roo.id(id);
15240         }
15241         delete this.invalidHandleIds[id];
15242     },
15243
15244     /**
15245      * Unsets an invalid css class
15246      * @method removeInvalidHandleClass
15247      * @param {string} cssClass the class of the element(s) you wish to
15248      * re-enable
15249      */
15250     removeInvalidHandleClass: function(cssClass) {
15251         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15252             if (this.invalidHandleClasses[i] == cssClass) {
15253                 delete this.invalidHandleClasses[i];
15254             }
15255         }
15256     },
15257
15258     /**
15259      * Checks the tag exclusion list to see if this click should be ignored
15260      * @method isValidHandleChild
15261      * @param {HTMLElement} node the HTMLElement to evaluate
15262      * @return {boolean} true if this is a valid tag type, false if not
15263      */
15264     isValidHandleChild: function(node) {
15265
15266         var valid = true;
15267         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15268         var nodeName;
15269         try {
15270             nodeName = node.nodeName.toUpperCase();
15271         } catch(e) {
15272             nodeName = node.nodeName;
15273         }
15274         valid = valid && !this.invalidHandleTypes[nodeName];
15275         valid = valid && !this.invalidHandleIds[node.id];
15276
15277         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15278             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15279         }
15280
15281
15282         return valid;
15283
15284     },
15285
15286     /**
15287      * Create the array of horizontal tick marks if an interval was specified
15288      * in setXConstraint().
15289      * @method setXTicks
15290      * @private
15291      */
15292     setXTicks: function(iStartX, iTickSize) {
15293         this.xTicks = [];
15294         this.xTickSize = iTickSize;
15295
15296         var tickMap = {};
15297
15298         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15299             if (!tickMap[i]) {
15300                 this.xTicks[this.xTicks.length] = i;
15301                 tickMap[i] = true;
15302             }
15303         }
15304
15305         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15306             if (!tickMap[i]) {
15307                 this.xTicks[this.xTicks.length] = i;
15308                 tickMap[i] = true;
15309             }
15310         }
15311
15312         this.xTicks.sort(this.DDM.numericSort) ;
15313     },
15314
15315     /**
15316      * Create the array of vertical tick marks if an interval was specified in
15317      * setYConstraint().
15318      * @method setYTicks
15319      * @private
15320      */
15321     setYTicks: function(iStartY, iTickSize) {
15322         this.yTicks = [];
15323         this.yTickSize = iTickSize;
15324
15325         var tickMap = {};
15326
15327         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15328             if (!tickMap[i]) {
15329                 this.yTicks[this.yTicks.length] = i;
15330                 tickMap[i] = true;
15331             }
15332         }
15333
15334         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15335             if (!tickMap[i]) {
15336                 this.yTicks[this.yTicks.length] = i;
15337                 tickMap[i] = true;
15338             }
15339         }
15340
15341         this.yTicks.sort(this.DDM.numericSort) ;
15342     },
15343
15344     /**
15345      * By default, the element can be dragged any place on the screen.  Use
15346      * this method to limit the horizontal travel of the element.  Pass in
15347      * 0,0 for the parameters if you want to lock the drag to the y axis.
15348      * @method setXConstraint
15349      * @param {int} iLeft the number of pixels the element can move to the left
15350      * @param {int} iRight the number of pixels the element can move to the
15351      * right
15352      * @param {int} iTickSize optional parameter for specifying that the
15353      * element
15354      * should move iTickSize pixels at a time.
15355      */
15356     setXConstraint: function(iLeft, iRight, iTickSize) {
15357         this.leftConstraint = iLeft;
15358         this.rightConstraint = iRight;
15359
15360         this.minX = this.initPageX - iLeft;
15361         this.maxX = this.initPageX + iRight;
15362         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15363
15364         this.constrainX = true;
15365     },
15366
15367     /**
15368      * Clears any constraints applied to this instance.  Also clears ticks
15369      * since they can't exist independent of a constraint at this time.
15370      * @method clearConstraints
15371      */
15372     clearConstraints: function() {
15373         this.constrainX = false;
15374         this.constrainY = false;
15375         this.clearTicks();
15376     },
15377
15378     /**
15379      * Clears any tick interval defined for this instance
15380      * @method clearTicks
15381      */
15382     clearTicks: function() {
15383         this.xTicks = null;
15384         this.yTicks = null;
15385         this.xTickSize = 0;
15386         this.yTickSize = 0;
15387     },
15388
15389     /**
15390      * By default, the element can be dragged any place on the screen.  Set
15391      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15392      * parameters if you want to lock the drag to the x axis.
15393      * @method setYConstraint
15394      * @param {int} iUp the number of pixels the element can move up
15395      * @param {int} iDown the number of pixels the element can move down
15396      * @param {int} iTickSize optional parameter for specifying that the
15397      * element should move iTickSize pixels at a time.
15398      */
15399     setYConstraint: function(iUp, iDown, iTickSize) {
15400         this.topConstraint = iUp;
15401         this.bottomConstraint = iDown;
15402
15403         this.minY = this.initPageY - iUp;
15404         this.maxY = this.initPageY + iDown;
15405         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15406
15407         this.constrainY = true;
15408
15409     },
15410
15411     /**
15412      * resetConstraints must be called if you manually reposition a dd element.
15413      * @method resetConstraints
15414      * @param {boolean} maintainOffset
15415      */
15416     resetConstraints: function() {
15417
15418
15419         // Maintain offsets if necessary
15420         if (this.initPageX || this.initPageX === 0) {
15421             // figure out how much this thing has moved
15422             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15423             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15424
15425             this.setInitPosition(dx, dy);
15426
15427         // This is the first time we have detected the element's position
15428         } else {
15429             this.setInitPosition();
15430         }
15431
15432         if (this.constrainX) {
15433             this.setXConstraint( this.leftConstraint,
15434                                  this.rightConstraint,
15435                                  this.xTickSize        );
15436         }
15437
15438         if (this.constrainY) {
15439             this.setYConstraint( this.topConstraint,
15440                                  this.bottomConstraint,
15441                                  this.yTickSize         );
15442         }
15443     },
15444
15445     /**
15446      * Normally the drag element is moved pixel by pixel, but we can specify
15447      * that it move a number of pixels at a time.  This method resolves the
15448      * location when we have it set up like this.
15449      * @method getTick
15450      * @param {int} val where we want to place the object
15451      * @param {int[]} tickArray sorted array of valid points
15452      * @return {int} the closest tick
15453      * @private
15454      */
15455     getTick: function(val, tickArray) {
15456
15457         if (!tickArray) {
15458             // If tick interval is not defined, it is effectively 1 pixel,
15459             // so we return the value passed to us.
15460             return val;
15461         } else if (tickArray[0] >= val) {
15462             // The value is lower than the first tick, so we return the first
15463             // tick.
15464             return tickArray[0];
15465         } else {
15466             for (var i=0, len=tickArray.length; i<len; ++i) {
15467                 var next = i + 1;
15468                 if (tickArray[next] && tickArray[next] >= val) {
15469                     var diff1 = val - tickArray[i];
15470                     var diff2 = tickArray[next] - val;
15471                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15472                 }
15473             }
15474
15475             // The value is larger than the last tick, so we return the last
15476             // tick.
15477             return tickArray[tickArray.length - 1];
15478         }
15479     },
15480
15481     /**
15482      * toString method
15483      * @method toString
15484      * @return {string} string representation of the dd obj
15485      */
15486     toString: function() {
15487         return ("DragDrop " + this.id);
15488     }
15489
15490 };
15491
15492 })();
15493 /*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503
15504
15505 /**
15506  * The drag and drop utility provides a framework for building drag and drop
15507  * applications.  In addition to enabling drag and drop for specific elements,
15508  * the drag and drop elements are tracked by the manager class, and the
15509  * interactions between the various elements are tracked during the drag and
15510  * the implementing code is notified about these important moments.
15511  */
15512
15513 // Only load the library once.  Rewriting the manager class would orphan
15514 // existing drag and drop instances.
15515 if (!Roo.dd.DragDropMgr) {
15516
15517 /**
15518  * @class Roo.dd.DragDropMgr
15519  * DragDropMgr is a singleton that tracks the element interaction for
15520  * all DragDrop items in the window.  Generally, you will not call
15521  * this class directly, but it does have helper methods that could
15522  * be useful in your DragDrop implementations.
15523  * @singleton
15524  */
15525 Roo.dd.DragDropMgr = function() {
15526
15527     var Event = Roo.EventManager;
15528
15529     return {
15530
15531         /**
15532          * Two dimensional Array of registered DragDrop objects.  The first
15533          * dimension is the DragDrop item group, the second the DragDrop
15534          * object.
15535          * @property ids
15536          * @type {string: string}
15537          * @private
15538          * @static
15539          */
15540         ids: {},
15541
15542         /**
15543          * Array of element ids defined as drag handles.  Used to determine
15544          * if the element that generated the mousedown event is actually the
15545          * handle and not the html element itself.
15546          * @property handleIds
15547          * @type {string: string}
15548          * @private
15549          * @static
15550          */
15551         handleIds: {},
15552
15553         /**
15554          * the DragDrop object that is currently being dragged
15555          * @property dragCurrent
15556          * @type DragDrop
15557          * @private
15558          * @static
15559          **/
15560         dragCurrent: null,
15561
15562         /**
15563          * the DragDrop object(s) that are being hovered over
15564          * @property dragOvers
15565          * @type Array
15566          * @private
15567          * @static
15568          */
15569         dragOvers: {},
15570
15571         /**
15572          * the X distance between the cursor and the object being dragged
15573          * @property deltaX
15574          * @type int
15575          * @private
15576          * @static
15577          */
15578         deltaX: 0,
15579
15580         /**
15581          * the Y distance between the cursor and the object being dragged
15582          * @property deltaY
15583          * @type int
15584          * @private
15585          * @static
15586          */
15587         deltaY: 0,
15588
15589         /**
15590          * Flag to determine if we should prevent the default behavior of the
15591          * events we define. By default this is true, but this can be set to
15592          * false if you need the default behavior (not recommended)
15593          * @property preventDefault
15594          * @type boolean
15595          * @static
15596          */
15597         preventDefault: true,
15598
15599         /**
15600          * Flag to determine if we should stop the propagation of the events
15601          * we generate. This is true by default but you may want to set it to
15602          * false if the html element contains other features that require the
15603          * mouse click.
15604          * @property stopPropagation
15605          * @type boolean
15606          * @static
15607          */
15608         stopPropagation: true,
15609
15610         /**
15611          * Internal flag that is set to true when drag and drop has been
15612          * intialized
15613          * @property initialized
15614          * @private
15615          * @static
15616          */
15617         initalized: false,
15618
15619         /**
15620          * All drag and drop can be disabled.
15621          * @property locked
15622          * @private
15623          * @static
15624          */
15625         locked: false,
15626
15627         /**
15628          * Called the first time an element is registered.
15629          * @method init
15630          * @private
15631          * @static
15632          */
15633         init: function() {
15634             this.initialized = true;
15635         },
15636
15637         /**
15638          * In point mode, drag and drop interaction is defined by the
15639          * location of the cursor during the drag/drop
15640          * @property POINT
15641          * @type int
15642          * @static
15643          */
15644         POINT: 0,
15645
15646         /**
15647          * In intersect mode, drag and drop interactio nis defined by the
15648          * overlap of two or more drag and drop objects.
15649          * @property INTERSECT
15650          * @type int
15651          * @static
15652          */
15653         INTERSECT: 1,
15654
15655         /**
15656          * The current drag and drop mode.  Default: POINT
15657          * @property mode
15658          * @type int
15659          * @static
15660          */
15661         mode: 0,
15662
15663         /**
15664          * Runs method on all drag and drop objects
15665          * @method _execOnAll
15666          * @private
15667          * @static
15668          */
15669         _execOnAll: function(sMethod, args) {
15670             for (var i in this.ids) {
15671                 for (var j in this.ids[i]) {
15672                     var oDD = this.ids[i][j];
15673                     if (! this.isTypeOfDD(oDD)) {
15674                         continue;
15675                     }
15676                     oDD[sMethod].apply(oDD, args);
15677                 }
15678             }
15679         },
15680
15681         /**
15682          * Drag and drop initialization.  Sets up the global event handlers
15683          * @method _onLoad
15684          * @private
15685          * @static
15686          */
15687         _onLoad: function() {
15688
15689             this.init();
15690
15691
15692             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15693             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15694             Event.on(window,   "unload",    this._onUnload, this, true);
15695             Event.on(window,   "resize",    this._onResize, this, true);
15696             // Event.on(window,   "mouseout",    this._test);
15697
15698         },
15699
15700         /**
15701          * Reset constraints on all drag and drop objs
15702          * @method _onResize
15703          * @private
15704          * @static
15705          */
15706         _onResize: function(e) {
15707             this._execOnAll("resetConstraints", []);
15708         },
15709
15710         /**
15711          * Lock all drag and drop functionality
15712          * @method lock
15713          * @static
15714          */
15715         lock: function() { this.locked = true; },
15716
15717         /**
15718          * Unlock all drag and drop functionality
15719          * @method unlock
15720          * @static
15721          */
15722         unlock: function() { this.locked = false; },
15723
15724         /**
15725          * Is drag and drop locked?
15726          * @method isLocked
15727          * @return {boolean} True if drag and drop is locked, false otherwise.
15728          * @static
15729          */
15730         isLocked: function() { return this.locked; },
15731
15732         /**
15733          * Location cache that is set for all drag drop objects when a drag is
15734          * initiated, cleared when the drag is finished.
15735          * @property locationCache
15736          * @private
15737          * @static
15738          */
15739         locationCache: {},
15740
15741         /**
15742          * Set useCache to false if you want to force object the lookup of each
15743          * drag and drop linked element constantly during a drag.
15744          * @property useCache
15745          * @type boolean
15746          * @static
15747          */
15748         useCache: true,
15749
15750         /**
15751          * The number of pixels that the mouse needs to move after the
15752          * mousedown before the drag is initiated.  Default=3;
15753          * @property clickPixelThresh
15754          * @type int
15755          * @static
15756          */
15757         clickPixelThresh: 3,
15758
15759         /**
15760          * The number of milliseconds after the mousedown event to initiate the
15761          * drag if we don't get a mouseup event. Default=1000
15762          * @property clickTimeThresh
15763          * @type int
15764          * @static
15765          */
15766         clickTimeThresh: 350,
15767
15768         /**
15769          * Flag that indicates that either the drag pixel threshold or the
15770          * mousdown time threshold has been met
15771          * @property dragThreshMet
15772          * @type boolean
15773          * @private
15774          * @static
15775          */
15776         dragThreshMet: false,
15777
15778         /**
15779          * Timeout used for the click time threshold
15780          * @property clickTimeout
15781          * @type Object
15782          * @private
15783          * @static
15784          */
15785         clickTimeout: null,
15786
15787         /**
15788          * The X position of the mousedown event stored for later use when a
15789          * drag threshold is met.
15790          * @property startX
15791          * @type int
15792          * @private
15793          * @static
15794          */
15795         startX: 0,
15796
15797         /**
15798          * The Y position of the mousedown event stored for later use when a
15799          * drag threshold is met.
15800          * @property startY
15801          * @type int
15802          * @private
15803          * @static
15804          */
15805         startY: 0,
15806
15807         /**
15808          * Each DragDrop instance must be registered with the DragDropMgr.
15809          * This is executed in DragDrop.init()
15810          * @method regDragDrop
15811          * @param {DragDrop} oDD the DragDrop object to register
15812          * @param {String} sGroup the name of the group this element belongs to
15813          * @static
15814          */
15815         regDragDrop: function(oDD, sGroup) {
15816             if (!this.initialized) { this.init(); }
15817
15818             if (!this.ids[sGroup]) {
15819                 this.ids[sGroup] = {};
15820             }
15821             this.ids[sGroup][oDD.id] = oDD;
15822         },
15823
15824         /**
15825          * Removes the supplied dd instance from the supplied group. Executed
15826          * by DragDrop.removeFromGroup, so don't call this function directly.
15827          * @method removeDDFromGroup
15828          * @private
15829          * @static
15830          */
15831         removeDDFromGroup: function(oDD, sGroup) {
15832             if (!this.ids[sGroup]) {
15833                 this.ids[sGroup] = {};
15834             }
15835
15836             var obj = this.ids[sGroup];
15837             if (obj && obj[oDD.id]) {
15838                 delete obj[oDD.id];
15839             }
15840         },
15841
15842         /**
15843          * Unregisters a drag and drop item.  This is executed in
15844          * DragDrop.unreg, use that method instead of calling this directly.
15845          * @method _remove
15846          * @private
15847          * @static
15848          */
15849         _remove: function(oDD) {
15850             for (var g in oDD.groups) {
15851                 if (g && this.ids[g][oDD.id]) {
15852                     delete this.ids[g][oDD.id];
15853                 }
15854             }
15855             delete this.handleIds[oDD.id];
15856         },
15857
15858         /**
15859          * Each DragDrop handle element must be registered.  This is done
15860          * automatically when executing DragDrop.setHandleElId()
15861          * @method regHandle
15862          * @param {String} sDDId the DragDrop id this element is a handle for
15863          * @param {String} sHandleId the id of the element that is the drag
15864          * handle
15865          * @static
15866          */
15867         regHandle: function(sDDId, sHandleId) {
15868             if (!this.handleIds[sDDId]) {
15869                 this.handleIds[sDDId] = {};
15870             }
15871             this.handleIds[sDDId][sHandleId] = sHandleId;
15872         },
15873
15874         /**
15875          * Utility function to determine if a given element has been
15876          * registered as a drag drop item.
15877          * @method isDragDrop
15878          * @param {String} id the element id to check
15879          * @return {boolean} true if this element is a DragDrop item,
15880          * false otherwise
15881          * @static
15882          */
15883         isDragDrop: function(id) {
15884             return ( this.getDDById(id) ) ? true : false;
15885         },
15886
15887         /**
15888          * Returns the drag and drop instances that are in all groups the
15889          * passed in instance belongs to.
15890          * @method getRelated
15891          * @param {DragDrop} p_oDD the obj to get related data for
15892          * @param {boolean} bTargetsOnly if true, only return targetable objs
15893          * @return {DragDrop[]} the related instances
15894          * @static
15895          */
15896         getRelated: function(p_oDD, bTargetsOnly) {
15897             var oDDs = [];
15898             for (var i in p_oDD.groups) {
15899                 for (j in this.ids[i]) {
15900                     var dd = this.ids[i][j];
15901                     if (! this.isTypeOfDD(dd)) {
15902                         continue;
15903                     }
15904                     if (!bTargetsOnly || dd.isTarget) {
15905                         oDDs[oDDs.length] = dd;
15906                     }
15907                 }
15908             }
15909
15910             return oDDs;
15911         },
15912
15913         /**
15914          * Returns true if the specified dd target is a legal target for
15915          * the specifice drag obj
15916          * @method isLegalTarget
15917          * @param {DragDrop} the drag obj
15918          * @param {DragDrop} the target
15919          * @return {boolean} true if the target is a legal target for the
15920          * dd obj
15921          * @static
15922          */
15923         isLegalTarget: function (oDD, oTargetDD) {
15924             var targets = this.getRelated(oDD, true);
15925             for (var i=0, len=targets.length;i<len;++i) {
15926                 if (targets[i].id == oTargetDD.id) {
15927                     return true;
15928                 }
15929             }
15930
15931             return false;
15932         },
15933
15934         /**
15935          * My goal is to be able to transparently determine if an object is
15936          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15937          * returns "object", oDD.constructor.toString() always returns
15938          * "DragDrop" and not the name of the subclass.  So for now it just
15939          * evaluates a well-known variable in DragDrop.
15940          * @method isTypeOfDD
15941          * @param {Object} the object to evaluate
15942          * @return {boolean} true if typeof oDD = DragDrop
15943          * @static
15944          */
15945         isTypeOfDD: function (oDD) {
15946             return (oDD && oDD.__ygDragDrop);
15947         },
15948
15949         /**
15950          * Utility function to determine if a given element has been
15951          * registered as a drag drop handle for the given Drag Drop object.
15952          * @method isHandle
15953          * @param {String} id the element id to check
15954          * @return {boolean} true if this element is a DragDrop handle, false
15955          * otherwise
15956          * @static
15957          */
15958         isHandle: function(sDDId, sHandleId) {
15959             return ( this.handleIds[sDDId] &&
15960                             this.handleIds[sDDId][sHandleId] );
15961         },
15962
15963         /**
15964          * Returns the DragDrop instance for a given id
15965          * @method getDDById
15966          * @param {String} id the id of the DragDrop object
15967          * @return {DragDrop} the drag drop object, null if it is not found
15968          * @static
15969          */
15970         getDDById: function(id) {
15971             for (var i in this.ids) {
15972                 if (this.ids[i][id]) {
15973                     return this.ids[i][id];
15974                 }
15975             }
15976             return null;
15977         },
15978
15979         /**
15980          * Fired after a registered DragDrop object gets the mousedown event.
15981          * Sets up the events required to track the object being dragged
15982          * @method handleMouseDown
15983          * @param {Event} e the event
15984          * @param oDD the DragDrop object being dragged
15985          * @private
15986          * @static
15987          */
15988         handleMouseDown: function(e, oDD) {
15989             if(Roo.QuickTips){
15990                 Roo.QuickTips.disable();
15991             }
15992             this.currentTarget = e.getTarget();
15993
15994             this.dragCurrent = oDD;
15995
15996             var el = oDD.getEl();
15997
15998             // track start position
15999             this.startX = e.getPageX();
16000             this.startY = e.getPageY();
16001
16002             this.deltaX = this.startX - el.offsetLeft;
16003             this.deltaY = this.startY - el.offsetTop;
16004
16005             this.dragThreshMet = false;
16006
16007             this.clickTimeout = setTimeout(
16008                     function() {
16009                         var DDM = Roo.dd.DDM;
16010                         DDM.startDrag(DDM.startX, DDM.startY);
16011                     },
16012                     this.clickTimeThresh );
16013         },
16014
16015         /**
16016          * Fired when either the drag pixel threshol or the mousedown hold
16017          * time threshold has been met.
16018          * @method startDrag
16019          * @param x {int} the X position of the original mousedown
16020          * @param y {int} the Y position of the original mousedown
16021          * @static
16022          */
16023         startDrag: function(x, y) {
16024             clearTimeout(this.clickTimeout);
16025             if (this.dragCurrent) {
16026                 this.dragCurrent.b4StartDrag(x, y);
16027                 this.dragCurrent.startDrag(x, y);
16028             }
16029             this.dragThreshMet = true;
16030         },
16031
16032         /**
16033          * Internal function to handle the mouseup event.  Will be invoked
16034          * from the context of the document.
16035          * @method handleMouseUp
16036          * @param {Event} e the event
16037          * @private
16038          * @static
16039          */
16040         handleMouseUp: function(e) {
16041
16042             if(Roo.QuickTips){
16043                 Roo.QuickTips.enable();
16044             }
16045             if (! this.dragCurrent) {
16046                 return;
16047             }
16048
16049             clearTimeout(this.clickTimeout);
16050
16051             if (this.dragThreshMet) {
16052                 this.fireEvents(e, true);
16053             } else {
16054             }
16055
16056             this.stopDrag(e);
16057
16058             this.stopEvent(e);
16059         },
16060
16061         /**
16062          * Utility to stop event propagation and event default, if these
16063          * features are turned on.
16064          * @method stopEvent
16065          * @param {Event} e the event as returned by this.getEvent()
16066          * @static
16067          */
16068         stopEvent: function(e){
16069             if(this.stopPropagation) {
16070                 e.stopPropagation();
16071             }
16072
16073             if (this.preventDefault) {
16074                 e.preventDefault();
16075             }
16076         },
16077
16078         /**
16079          * Internal function to clean up event handlers after the drag
16080          * operation is complete
16081          * @method stopDrag
16082          * @param {Event} e the event
16083          * @private
16084          * @static
16085          */
16086         stopDrag: function(e) {
16087             // Fire the drag end event for the item that was dragged
16088             if (this.dragCurrent) {
16089                 if (this.dragThreshMet) {
16090                     this.dragCurrent.b4EndDrag(e);
16091                     this.dragCurrent.endDrag(e);
16092                 }
16093
16094                 this.dragCurrent.onMouseUp(e);
16095             }
16096
16097             this.dragCurrent = null;
16098             this.dragOvers = {};
16099         },
16100
16101         /**
16102          * Internal function to handle the mousemove event.  Will be invoked
16103          * from the context of the html element.
16104          *
16105          * @TODO figure out what we can do about mouse events lost when the
16106          * user drags objects beyond the window boundary.  Currently we can
16107          * detect this in internet explorer by verifying that the mouse is
16108          * down during the mousemove event.  Firefox doesn't give us the
16109          * button state on the mousemove event.
16110          * @method handleMouseMove
16111          * @param {Event} e the event
16112          * @private
16113          * @static
16114          */
16115         handleMouseMove: function(e) {
16116             if (! this.dragCurrent) {
16117                 return true;
16118             }
16119
16120             // var button = e.which || e.button;
16121
16122             // check for IE mouseup outside of page boundary
16123             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16124                 this.stopEvent(e);
16125                 return this.handleMouseUp(e);
16126             }
16127
16128             if (!this.dragThreshMet) {
16129                 var diffX = Math.abs(this.startX - e.getPageX());
16130                 var diffY = Math.abs(this.startY - e.getPageY());
16131                 if (diffX > this.clickPixelThresh ||
16132                             diffY > this.clickPixelThresh) {
16133                     this.startDrag(this.startX, this.startY);
16134                 }
16135             }
16136
16137             if (this.dragThreshMet) {
16138                 this.dragCurrent.b4Drag(e);
16139                 this.dragCurrent.onDrag(e);
16140                 if(!this.dragCurrent.moveOnly){
16141                     this.fireEvents(e, false);
16142                 }
16143             }
16144
16145             this.stopEvent(e);
16146
16147             return true;
16148         },
16149
16150         /**
16151          * Iterates over all of the DragDrop elements to find ones we are
16152          * hovering over or dropping on
16153          * @method fireEvents
16154          * @param {Event} e the event
16155          * @param {boolean} isDrop is this a drop op or a mouseover op?
16156          * @private
16157          * @static
16158          */
16159         fireEvents: function(e, isDrop) {
16160             var dc = this.dragCurrent;
16161
16162             // If the user did the mouse up outside of the window, we could
16163             // get here even though we have ended the drag.
16164             if (!dc || dc.isLocked()) {
16165                 return;
16166             }
16167
16168             var pt = e.getPoint();
16169
16170             // cache the previous dragOver array
16171             var oldOvers = [];
16172
16173             var outEvts   = [];
16174             var overEvts  = [];
16175             var dropEvts  = [];
16176             var enterEvts = [];
16177
16178             // Check to see if the object(s) we were hovering over is no longer
16179             // being hovered over so we can fire the onDragOut event
16180             for (var i in this.dragOvers) {
16181
16182                 var ddo = this.dragOvers[i];
16183
16184                 if (! this.isTypeOfDD(ddo)) {
16185                     continue;
16186                 }
16187
16188                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16189                     outEvts.push( ddo );
16190                 }
16191
16192                 oldOvers[i] = true;
16193                 delete this.dragOvers[i];
16194             }
16195
16196             for (var sGroup in dc.groups) {
16197
16198                 if ("string" != typeof sGroup) {
16199                     continue;
16200                 }
16201
16202                 for (i in this.ids[sGroup]) {
16203                     var oDD = this.ids[sGroup][i];
16204                     if (! this.isTypeOfDD(oDD)) {
16205                         continue;
16206                     }
16207
16208                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16209                         if (this.isOverTarget(pt, oDD, this.mode)) {
16210                             // look for drop interactions
16211                             if (isDrop) {
16212                                 dropEvts.push( oDD );
16213                             // look for drag enter and drag over interactions
16214                             } else {
16215
16216                                 // initial drag over: dragEnter fires
16217                                 if (!oldOvers[oDD.id]) {
16218                                     enterEvts.push( oDD );
16219                                 // subsequent drag overs: dragOver fires
16220                                 } else {
16221                                     overEvts.push( oDD );
16222                                 }
16223
16224                                 this.dragOvers[oDD.id] = oDD;
16225                             }
16226                         }
16227                     }
16228                 }
16229             }
16230
16231             if (this.mode) {
16232                 if (outEvts.length) {
16233                     dc.b4DragOut(e, outEvts);
16234                     dc.onDragOut(e, outEvts);
16235                 }
16236
16237                 if (enterEvts.length) {
16238                     dc.onDragEnter(e, enterEvts);
16239                 }
16240
16241                 if (overEvts.length) {
16242                     dc.b4DragOver(e, overEvts);
16243                     dc.onDragOver(e, overEvts);
16244                 }
16245
16246                 if (dropEvts.length) {
16247                     dc.b4DragDrop(e, dropEvts);
16248                     dc.onDragDrop(e, dropEvts);
16249                 }
16250
16251             } else {
16252                 // fire dragout events
16253                 var len = 0;
16254                 for (i=0, len=outEvts.length; i<len; ++i) {
16255                     dc.b4DragOut(e, outEvts[i].id);
16256                     dc.onDragOut(e, outEvts[i].id);
16257                 }
16258
16259                 // fire enter events
16260                 for (i=0,len=enterEvts.length; i<len; ++i) {
16261                     // dc.b4DragEnter(e, oDD.id);
16262                     dc.onDragEnter(e, enterEvts[i].id);
16263                 }
16264
16265                 // fire over events
16266                 for (i=0,len=overEvts.length; i<len; ++i) {
16267                     dc.b4DragOver(e, overEvts[i].id);
16268                     dc.onDragOver(e, overEvts[i].id);
16269                 }
16270
16271                 // fire drop events
16272                 for (i=0, len=dropEvts.length; i<len; ++i) {
16273                     dc.b4DragDrop(e, dropEvts[i].id);
16274                     dc.onDragDrop(e, dropEvts[i].id);
16275                 }
16276
16277             }
16278
16279             // notify about a drop that did not find a target
16280             if (isDrop && !dropEvts.length) {
16281                 dc.onInvalidDrop(e);
16282             }
16283
16284         },
16285
16286         /**
16287          * Helper function for getting the best match from the list of drag
16288          * and drop objects returned by the drag and drop events when we are
16289          * in INTERSECT mode.  It returns either the first object that the
16290          * cursor is over, or the object that has the greatest overlap with
16291          * the dragged element.
16292          * @method getBestMatch
16293          * @param  {DragDrop[]} dds The array of drag and drop objects
16294          * targeted
16295          * @return {DragDrop}       The best single match
16296          * @static
16297          */
16298         getBestMatch: function(dds) {
16299             var winner = null;
16300             // Return null if the input is not what we expect
16301             //if (!dds || !dds.length || dds.length == 0) {
16302                // winner = null;
16303             // If there is only one item, it wins
16304             //} else if (dds.length == 1) {
16305
16306             var len = dds.length;
16307
16308             if (len == 1) {
16309                 winner = dds[0];
16310             } else {
16311                 // Loop through the targeted items
16312                 for (var i=0; i<len; ++i) {
16313                     var dd = dds[i];
16314                     // If the cursor is over the object, it wins.  If the
16315                     // cursor is over multiple matches, the first one we come
16316                     // to wins.
16317                     if (dd.cursorIsOver) {
16318                         winner = dd;
16319                         break;
16320                     // Otherwise the object with the most overlap wins
16321                     } else {
16322                         if (!winner ||
16323                             winner.overlap.getArea() < dd.overlap.getArea()) {
16324                             winner = dd;
16325                         }
16326                     }
16327                 }
16328             }
16329
16330             return winner;
16331         },
16332
16333         /**
16334          * Refreshes the cache of the top-left and bottom-right points of the
16335          * drag and drop objects in the specified group(s).  This is in the
16336          * format that is stored in the drag and drop instance, so typical
16337          * usage is:
16338          * <code>
16339          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16340          * </code>
16341          * Alternatively:
16342          * <code>
16343          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16344          * </code>
16345          * @TODO this really should be an indexed array.  Alternatively this
16346          * method could accept both.
16347          * @method refreshCache
16348          * @param {Object} groups an associative array of groups to refresh
16349          * @static
16350          */
16351         refreshCache: function(groups) {
16352             for (var sGroup in groups) {
16353                 if ("string" != typeof sGroup) {
16354                     continue;
16355                 }
16356                 for (var i in this.ids[sGroup]) {
16357                     var oDD = this.ids[sGroup][i];
16358
16359                     if (this.isTypeOfDD(oDD)) {
16360                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16361                         var loc = this.getLocation(oDD);
16362                         if (loc) {
16363                             this.locationCache[oDD.id] = loc;
16364                         } else {
16365                             delete this.locationCache[oDD.id];
16366                             // this will unregister the drag and drop object if
16367                             // the element is not in a usable state
16368                             // oDD.unreg();
16369                         }
16370                     }
16371                 }
16372             }
16373         },
16374
16375         /**
16376          * This checks to make sure an element exists and is in the DOM.  The
16377          * main purpose is to handle cases where innerHTML is used to remove
16378          * drag and drop objects from the DOM.  IE provides an 'unspecified
16379          * error' when trying to access the offsetParent of such an element
16380          * @method verifyEl
16381          * @param {HTMLElement} el the element to check
16382          * @return {boolean} true if the element looks usable
16383          * @static
16384          */
16385         verifyEl: function(el) {
16386             if (el) {
16387                 var parent;
16388                 if(Roo.isIE){
16389                     try{
16390                         parent = el.offsetParent;
16391                     }catch(e){}
16392                 }else{
16393                     parent = el.offsetParent;
16394                 }
16395                 if (parent) {
16396                     return true;
16397                 }
16398             }
16399
16400             return false;
16401         },
16402
16403         /**
16404          * Returns a Region object containing the drag and drop element's position
16405          * and size, including the padding configured for it
16406          * @method getLocation
16407          * @param {DragDrop} oDD the drag and drop object to get the
16408          *                       location for
16409          * @return {Roo.lib.Region} a Region object representing the total area
16410          *                             the element occupies, including any padding
16411          *                             the instance is configured for.
16412          * @static
16413          */
16414         getLocation: function(oDD) {
16415             if (! this.isTypeOfDD(oDD)) {
16416                 return null;
16417             }
16418
16419             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16420
16421             try {
16422                 pos= Roo.lib.Dom.getXY(el);
16423             } catch (e) { }
16424
16425             if (!pos) {
16426                 return null;
16427             }
16428
16429             x1 = pos[0];
16430             x2 = x1 + el.offsetWidth;
16431             y1 = pos[1];
16432             y2 = y1 + el.offsetHeight;
16433
16434             t = y1 - oDD.padding[0];
16435             r = x2 + oDD.padding[1];
16436             b = y2 + oDD.padding[2];
16437             l = x1 - oDD.padding[3];
16438
16439             return new Roo.lib.Region( t, r, b, l );
16440         },
16441
16442         /**
16443          * Checks the cursor location to see if it over the target
16444          * @method isOverTarget
16445          * @param {Roo.lib.Point} pt The point to evaluate
16446          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16447          * @return {boolean} true if the mouse is over the target
16448          * @private
16449          * @static
16450          */
16451         isOverTarget: function(pt, oTarget, intersect) {
16452             // use cache if available
16453             var loc = this.locationCache[oTarget.id];
16454             if (!loc || !this.useCache) {
16455                 loc = this.getLocation(oTarget);
16456                 this.locationCache[oTarget.id] = loc;
16457
16458             }
16459
16460             if (!loc) {
16461                 return false;
16462             }
16463
16464             oTarget.cursorIsOver = loc.contains( pt );
16465
16466             // DragDrop is using this as a sanity check for the initial mousedown
16467             // in this case we are done.  In POINT mode, if the drag obj has no
16468             // contraints, we are also done. Otherwise we need to evaluate the
16469             // location of the target as related to the actual location of the
16470             // dragged element.
16471             var dc = this.dragCurrent;
16472             if (!dc || !dc.getTargetCoord ||
16473                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16474                 return oTarget.cursorIsOver;
16475             }
16476
16477             oTarget.overlap = null;
16478
16479             // Get the current location of the drag element, this is the
16480             // location of the mouse event less the delta that represents
16481             // where the original mousedown happened on the element.  We
16482             // need to consider constraints and ticks as well.
16483             var pos = dc.getTargetCoord(pt.x, pt.y);
16484
16485             var el = dc.getDragEl();
16486             var curRegion = new Roo.lib.Region( pos.y,
16487                                                    pos.x + el.offsetWidth,
16488                                                    pos.y + el.offsetHeight,
16489                                                    pos.x );
16490
16491             var overlap = curRegion.intersect(loc);
16492
16493             if (overlap) {
16494                 oTarget.overlap = overlap;
16495                 return (intersect) ? true : oTarget.cursorIsOver;
16496             } else {
16497                 return false;
16498             }
16499         },
16500
16501         /**
16502          * unload event handler
16503          * @method _onUnload
16504          * @private
16505          * @static
16506          */
16507         _onUnload: function(e, me) {
16508             Roo.dd.DragDropMgr.unregAll();
16509         },
16510
16511         /**
16512          * Cleans up the drag and drop events and objects.
16513          * @method unregAll
16514          * @private
16515          * @static
16516          */
16517         unregAll: function() {
16518
16519             if (this.dragCurrent) {
16520                 this.stopDrag();
16521                 this.dragCurrent = null;
16522             }
16523
16524             this._execOnAll("unreg", []);
16525
16526             for (i in this.elementCache) {
16527                 delete this.elementCache[i];
16528             }
16529
16530             this.elementCache = {};
16531             this.ids = {};
16532         },
16533
16534         /**
16535          * A cache of DOM elements
16536          * @property elementCache
16537          * @private
16538          * @static
16539          */
16540         elementCache: {},
16541
16542         /**
16543          * Get the wrapper for the DOM element specified
16544          * @method getElWrapper
16545          * @param {String} id the id of the element to get
16546          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16547          * @private
16548          * @deprecated This wrapper isn't that useful
16549          * @static
16550          */
16551         getElWrapper: function(id) {
16552             var oWrapper = this.elementCache[id];
16553             if (!oWrapper || !oWrapper.el) {
16554                 oWrapper = this.elementCache[id] =
16555                     new this.ElementWrapper(Roo.getDom(id));
16556             }
16557             return oWrapper;
16558         },
16559
16560         /**
16561          * Returns the actual DOM element
16562          * @method getElement
16563          * @param {String} id the id of the elment to get
16564          * @return {Object} The element
16565          * @deprecated use Roo.getDom instead
16566          * @static
16567          */
16568         getElement: function(id) {
16569             return Roo.getDom(id);
16570         },
16571
16572         /**
16573          * Returns the style property for the DOM element (i.e.,
16574          * document.getElById(id).style)
16575          * @method getCss
16576          * @param {String} id the id of the elment to get
16577          * @return {Object} The style property of the element
16578          * @deprecated use Roo.getDom instead
16579          * @static
16580          */
16581         getCss: function(id) {
16582             var el = Roo.getDom(id);
16583             return (el) ? el.style : null;
16584         },
16585
16586         /**
16587          * Inner class for cached elements
16588          * @class DragDropMgr.ElementWrapper
16589          * @for DragDropMgr
16590          * @private
16591          * @deprecated
16592          */
16593         ElementWrapper: function(el) {
16594                 /**
16595                  * The element
16596                  * @property el
16597                  */
16598                 this.el = el || null;
16599                 /**
16600                  * The element id
16601                  * @property id
16602                  */
16603                 this.id = this.el && el.id;
16604                 /**
16605                  * A reference to the style property
16606                  * @property css
16607                  */
16608                 this.css = this.el && el.style;
16609             },
16610
16611         /**
16612          * Returns the X position of an html element
16613          * @method getPosX
16614          * @param el the element for which to get the position
16615          * @return {int} the X coordinate
16616          * @for DragDropMgr
16617          * @deprecated use Roo.lib.Dom.getX instead
16618          * @static
16619          */
16620         getPosX: function(el) {
16621             return Roo.lib.Dom.getX(el);
16622         },
16623
16624         /**
16625          * Returns the Y position of an html element
16626          * @method getPosY
16627          * @param el the element for which to get the position
16628          * @return {int} the Y coordinate
16629          * @deprecated use Roo.lib.Dom.getY instead
16630          * @static
16631          */
16632         getPosY: function(el) {
16633             return Roo.lib.Dom.getY(el);
16634         },
16635
16636         /**
16637          * Swap two nodes.  In IE, we use the native method, for others we
16638          * emulate the IE behavior
16639          * @method swapNode
16640          * @param n1 the first node to swap
16641          * @param n2 the other node to swap
16642          * @static
16643          */
16644         swapNode: function(n1, n2) {
16645             if (n1.swapNode) {
16646                 n1.swapNode(n2);
16647             } else {
16648                 var p = n2.parentNode;
16649                 var s = n2.nextSibling;
16650
16651                 if (s == n1) {
16652                     p.insertBefore(n1, n2);
16653                 } else if (n2 == n1.nextSibling) {
16654                     p.insertBefore(n2, n1);
16655                 } else {
16656                     n1.parentNode.replaceChild(n2, n1);
16657                     p.insertBefore(n1, s);
16658                 }
16659             }
16660         },
16661
16662         /**
16663          * Returns the current scroll position
16664          * @method getScroll
16665          * @private
16666          * @static
16667          */
16668         getScroll: function () {
16669             var t, l, dde=document.documentElement, db=document.body;
16670             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16671                 t = dde.scrollTop;
16672                 l = dde.scrollLeft;
16673             } else if (db) {
16674                 t = db.scrollTop;
16675                 l = db.scrollLeft;
16676             } else {
16677
16678             }
16679             return { top: t, left: l };
16680         },
16681
16682         /**
16683          * Returns the specified element style property
16684          * @method getStyle
16685          * @param {HTMLElement} el          the element
16686          * @param {string}      styleProp   the style property
16687          * @return {string} The value of the style property
16688          * @deprecated use Roo.lib.Dom.getStyle
16689          * @static
16690          */
16691         getStyle: function(el, styleProp) {
16692             return Roo.fly(el).getStyle(styleProp);
16693         },
16694
16695         /**
16696          * Gets the scrollTop
16697          * @method getScrollTop
16698          * @return {int} the document's scrollTop
16699          * @static
16700          */
16701         getScrollTop: function () { return this.getScroll().top; },
16702
16703         /**
16704          * Gets the scrollLeft
16705          * @method getScrollLeft
16706          * @return {int} the document's scrollTop
16707          * @static
16708          */
16709         getScrollLeft: function () { return this.getScroll().left; },
16710
16711         /**
16712          * Sets the x/y position of an element to the location of the
16713          * target element.
16714          * @method moveToEl
16715          * @param {HTMLElement} moveEl      The element to move
16716          * @param {HTMLElement} targetEl    The position reference element
16717          * @static
16718          */
16719         moveToEl: function (moveEl, targetEl) {
16720             var aCoord = Roo.lib.Dom.getXY(targetEl);
16721             Roo.lib.Dom.setXY(moveEl, aCoord);
16722         },
16723
16724         /**
16725          * Numeric array sort function
16726          * @method numericSort
16727          * @static
16728          */
16729         numericSort: function(a, b) { return (a - b); },
16730
16731         /**
16732          * Internal counter
16733          * @property _timeoutCount
16734          * @private
16735          * @static
16736          */
16737         _timeoutCount: 0,
16738
16739         /**
16740          * Trying to make the load order less important.  Without this we get
16741          * an error if this file is loaded before the Event Utility.
16742          * @method _addListeners
16743          * @private
16744          * @static
16745          */
16746         _addListeners: function() {
16747             var DDM = Roo.dd.DDM;
16748             if ( Roo.lib.Event && document ) {
16749                 DDM._onLoad();
16750             } else {
16751                 if (DDM._timeoutCount > 2000) {
16752                 } else {
16753                     setTimeout(DDM._addListeners, 10);
16754                     if (document && document.body) {
16755                         DDM._timeoutCount += 1;
16756                     }
16757                 }
16758             }
16759         },
16760
16761         /**
16762          * Recursively searches the immediate parent and all child nodes for
16763          * the handle element in order to determine wheter or not it was
16764          * clicked.
16765          * @method handleWasClicked
16766          * @param node the html element to inspect
16767          * @static
16768          */
16769         handleWasClicked: function(node, id) {
16770             if (this.isHandle(id, node.id)) {
16771                 return true;
16772             } else {
16773                 // check to see if this is a text node child of the one we want
16774                 var p = node.parentNode;
16775
16776                 while (p) {
16777                     if (this.isHandle(id, p.id)) {
16778                         return true;
16779                     } else {
16780                         p = p.parentNode;
16781                     }
16782                 }
16783             }
16784
16785             return false;
16786         }
16787
16788     };
16789
16790 }();
16791
16792 // shorter alias, save a few bytes
16793 Roo.dd.DDM = Roo.dd.DragDropMgr;
16794 Roo.dd.DDM._addListeners();
16795
16796 }/*
16797  * Based on:
16798  * Ext JS Library 1.1.1
16799  * Copyright(c) 2006-2007, Ext JS, LLC.
16800  *
16801  * Originally Released Under LGPL - original licence link has changed is not relivant.
16802  *
16803  * Fork - LGPL
16804  * <script type="text/javascript">
16805  */
16806
16807 /**
16808  * @class Roo.dd.DD
16809  * A DragDrop implementation where the linked element follows the
16810  * mouse cursor during a drag.
16811  * @extends Roo.dd.DragDrop
16812  * @constructor
16813  * @param {String} id the id of the linked element
16814  * @param {String} sGroup the group of related DragDrop items
16815  * @param {object} config an object containing configurable attributes
16816  *                Valid properties for DD:
16817  *                    scroll
16818  */
16819 Roo.dd.DD = function(id, sGroup, config) {
16820     if (id) {
16821         this.init(id, sGroup, config);
16822     }
16823 };
16824
16825 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16826
16827     /**
16828      * When set to true, the utility automatically tries to scroll the browser
16829      * window wehn a drag and drop element is dragged near the viewport boundary.
16830      * Defaults to true.
16831      * @property scroll
16832      * @type boolean
16833      */
16834     scroll: true,
16835
16836     /**
16837      * Sets the pointer offset to the distance between the linked element's top
16838      * left corner and the location the element was clicked
16839      * @method autoOffset
16840      * @param {int} iPageX the X coordinate of the click
16841      * @param {int} iPageY the Y coordinate of the click
16842      */
16843     autoOffset: function(iPageX, iPageY) {
16844         var x = iPageX - this.startPageX;
16845         var y = iPageY - this.startPageY;
16846         this.setDelta(x, y);
16847     },
16848
16849     /**
16850      * Sets the pointer offset.  You can call this directly to force the
16851      * offset to be in a particular location (e.g., pass in 0,0 to set it
16852      * to the center of the object)
16853      * @method setDelta
16854      * @param {int} iDeltaX the distance from the left
16855      * @param {int} iDeltaY the distance from the top
16856      */
16857     setDelta: function(iDeltaX, iDeltaY) {
16858         this.deltaX = iDeltaX;
16859         this.deltaY = iDeltaY;
16860     },
16861
16862     /**
16863      * Sets the drag element to the location of the mousedown or click event,
16864      * maintaining the cursor location relative to the location on the element
16865      * that was clicked.  Override this if you want to place the element in a
16866      * location other than where the cursor is.
16867      * @method setDragElPos
16868      * @param {int} iPageX the X coordinate of the mousedown or drag event
16869      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16870      */
16871     setDragElPos: function(iPageX, iPageY) {
16872         // the first time we do this, we are going to check to make sure
16873         // the element has css positioning
16874
16875         var el = this.getDragEl();
16876         this.alignElWithMouse(el, iPageX, iPageY);
16877     },
16878
16879     /**
16880      * Sets the element to the location of the mousedown or click event,
16881      * maintaining the cursor location relative to the location on the element
16882      * that was clicked.  Override this if you want to place the element in a
16883      * location other than where the cursor is.
16884      * @method alignElWithMouse
16885      * @param {HTMLElement} el the element to move
16886      * @param {int} iPageX the X coordinate of the mousedown or drag event
16887      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16888      */
16889     alignElWithMouse: function(el, iPageX, iPageY) {
16890         var oCoord = this.getTargetCoord(iPageX, iPageY);
16891         var fly = el.dom ? el : Roo.fly(el);
16892         if (!this.deltaSetXY) {
16893             var aCoord = [oCoord.x, oCoord.y];
16894             fly.setXY(aCoord);
16895             var newLeft = fly.getLeft(true);
16896             var newTop  = fly.getTop(true);
16897             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16898         } else {
16899             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16900         }
16901
16902         this.cachePosition(oCoord.x, oCoord.y);
16903         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16904         return oCoord;
16905     },
16906
16907     /**
16908      * Saves the most recent position so that we can reset the constraints and
16909      * tick marks on-demand.  We need to know this so that we can calculate the
16910      * number of pixels the element is offset from its original position.
16911      * @method cachePosition
16912      * @param iPageX the current x position (optional, this just makes it so we
16913      * don't have to look it up again)
16914      * @param iPageY the current y position (optional, this just makes it so we
16915      * don't have to look it up again)
16916      */
16917     cachePosition: function(iPageX, iPageY) {
16918         if (iPageX) {
16919             this.lastPageX = iPageX;
16920             this.lastPageY = iPageY;
16921         } else {
16922             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16923             this.lastPageX = aCoord[0];
16924             this.lastPageY = aCoord[1];
16925         }
16926     },
16927
16928     /**
16929      * Auto-scroll the window if the dragged object has been moved beyond the
16930      * visible window boundary.
16931      * @method autoScroll
16932      * @param {int} x the drag element's x position
16933      * @param {int} y the drag element's y position
16934      * @param {int} h the height of the drag element
16935      * @param {int} w the width of the drag element
16936      * @private
16937      */
16938     autoScroll: function(x, y, h, w) {
16939
16940         if (this.scroll) {
16941             // The client height
16942             var clientH = Roo.lib.Dom.getViewWidth();
16943
16944             // The client width
16945             var clientW = Roo.lib.Dom.getViewHeight();
16946
16947             // The amt scrolled down
16948             var st = this.DDM.getScrollTop();
16949
16950             // The amt scrolled right
16951             var sl = this.DDM.getScrollLeft();
16952
16953             // Location of the bottom of the element
16954             var bot = h + y;
16955
16956             // Location of the right of the element
16957             var right = w + x;
16958
16959             // The distance from the cursor to the bottom of the visible area,
16960             // adjusted so that we don't scroll if the cursor is beyond the
16961             // element drag constraints
16962             var toBot = (clientH + st - y - this.deltaY);
16963
16964             // The distance from the cursor to the right of the visible area
16965             var toRight = (clientW + sl - x - this.deltaX);
16966
16967
16968             // How close to the edge the cursor must be before we scroll
16969             // var thresh = (document.all) ? 100 : 40;
16970             var thresh = 40;
16971
16972             // How many pixels to scroll per autoscroll op.  This helps to reduce
16973             // clunky scrolling. IE is more sensitive about this ... it needs this
16974             // value to be higher.
16975             var scrAmt = (document.all) ? 80 : 30;
16976
16977             // Scroll down if we are near the bottom of the visible page and the
16978             // obj extends below the crease
16979             if ( bot > clientH && toBot < thresh ) {
16980                 window.scrollTo(sl, st + scrAmt);
16981             }
16982
16983             // Scroll up if the window is scrolled down and the top of the object
16984             // goes above the top border
16985             if ( y < st && st > 0 && y - st < thresh ) {
16986                 window.scrollTo(sl, st - scrAmt);
16987             }
16988
16989             // Scroll right if the obj is beyond the right border and the cursor is
16990             // near the border.
16991             if ( right > clientW && toRight < thresh ) {
16992                 window.scrollTo(sl + scrAmt, st);
16993             }
16994
16995             // Scroll left if the window has been scrolled to the right and the obj
16996             // extends past the left border
16997             if ( x < sl && sl > 0 && x - sl < thresh ) {
16998                 window.scrollTo(sl - scrAmt, st);
16999             }
17000         }
17001     },
17002
17003     /**
17004      * Finds the location the element should be placed if we want to move
17005      * it to where the mouse location less the click offset would place us.
17006      * @method getTargetCoord
17007      * @param {int} iPageX the X coordinate of the click
17008      * @param {int} iPageY the Y coordinate of the click
17009      * @return an object that contains the coordinates (Object.x and Object.y)
17010      * @private
17011      */
17012     getTargetCoord: function(iPageX, iPageY) {
17013
17014
17015         var x = iPageX - this.deltaX;
17016         var y = iPageY - this.deltaY;
17017
17018         if (this.constrainX) {
17019             if (x < this.minX) { x = this.minX; }
17020             if (x > this.maxX) { x = this.maxX; }
17021         }
17022
17023         if (this.constrainY) {
17024             if (y < this.minY) { y = this.minY; }
17025             if (y > this.maxY) { y = this.maxY; }
17026         }
17027
17028         x = this.getTick(x, this.xTicks);
17029         y = this.getTick(y, this.yTicks);
17030
17031
17032         return {x:x, y:y};
17033     },
17034
17035     /*
17036      * Sets up config options specific to this class. Overrides
17037      * Roo.dd.DragDrop, but all versions of this method through the
17038      * inheritance chain are called
17039      */
17040     applyConfig: function() {
17041         Roo.dd.DD.superclass.applyConfig.call(this);
17042         this.scroll = (this.config.scroll !== false);
17043     },
17044
17045     /*
17046      * Event that fires prior to the onMouseDown event.  Overrides
17047      * Roo.dd.DragDrop.
17048      */
17049     b4MouseDown: function(e) {
17050         // this.resetConstraints();
17051         this.autoOffset(e.getPageX(),
17052                             e.getPageY());
17053     },
17054
17055     /*
17056      * Event that fires prior to the onDrag event.  Overrides
17057      * Roo.dd.DragDrop.
17058      */
17059     b4Drag: function(e) {
17060         this.setDragElPos(e.getPageX(),
17061                             e.getPageY());
17062     },
17063
17064     toString: function() {
17065         return ("DD " + this.id);
17066     }
17067
17068     //////////////////////////////////////////////////////////////////////////
17069     // Debugging ygDragDrop events that can be overridden
17070     //////////////////////////////////////////////////////////////////////////
17071     /*
17072     startDrag: function(x, y) {
17073     },
17074
17075     onDrag: function(e) {
17076     },
17077
17078     onDragEnter: function(e, id) {
17079     },
17080
17081     onDragOver: function(e, id) {
17082     },
17083
17084     onDragOut: function(e, id) {
17085     },
17086
17087     onDragDrop: function(e, id) {
17088     },
17089
17090     endDrag: function(e) {
17091     }
17092
17093     */
17094
17095 });/*
17096  * Based on:
17097  * Ext JS Library 1.1.1
17098  * Copyright(c) 2006-2007, Ext JS, LLC.
17099  *
17100  * Originally Released Under LGPL - original licence link has changed is not relivant.
17101  *
17102  * Fork - LGPL
17103  * <script type="text/javascript">
17104  */
17105
17106 /**
17107  * @class Roo.dd.DDProxy
17108  * A DragDrop implementation that inserts an empty, bordered div into
17109  * the document that follows the cursor during drag operations.  At the time of
17110  * the click, the frame div is resized to the dimensions of the linked html
17111  * element, and moved to the exact location of the linked element.
17112  *
17113  * References to the "frame" element refer to the single proxy element that
17114  * was created to be dragged in place of all DDProxy elements on the
17115  * page.
17116  *
17117  * @extends Roo.dd.DD
17118  * @constructor
17119  * @param {String} id the id of the linked html element
17120  * @param {String} sGroup the group of related DragDrop objects
17121  * @param {object} config an object containing configurable attributes
17122  *                Valid properties for DDProxy in addition to those in DragDrop:
17123  *                   resizeFrame, centerFrame, dragElId
17124  */
17125 Roo.dd.DDProxy = function(id, sGroup, config) {
17126     if (id) {
17127         this.init(id, sGroup, config);
17128         this.initFrame();
17129     }
17130 };
17131
17132 /**
17133  * The default drag frame div id
17134  * @property Roo.dd.DDProxy.dragElId
17135  * @type String
17136  * @static
17137  */
17138 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17139
17140 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17141
17142     /**
17143      * By default we resize the drag frame to be the same size as the element
17144      * we want to drag (this is to get the frame effect).  We can turn it off
17145      * if we want a different behavior.
17146      * @property resizeFrame
17147      * @type boolean
17148      */
17149     resizeFrame: true,
17150
17151     /**
17152      * By default the frame is positioned exactly where the drag element is, so
17153      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17154      * you do not have constraints on the obj is to have the drag frame centered
17155      * around the cursor.  Set centerFrame to true for this effect.
17156      * @property centerFrame
17157      * @type boolean
17158      */
17159     centerFrame: false,
17160
17161     /**
17162      * Creates the proxy element if it does not yet exist
17163      * @method createFrame
17164      */
17165     createFrame: function() {
17166         var self = this;
17167         var body = document.body;
17168
17169         if (!body || !body.firstChild) {
17170             setTimeout( function() { self.createFrame(); }, 50 );
17171             return;
17172         }
17173
17174         var div = this.getDragEl();
17175
17176         if (!div) {
17177             div    = document.createElement("div");
17178             div.id = this.dragElId;
17179             var s  = div.style;
17180
17181             s.position   = "absolute";
17182             s.visibility = "hidden";
17183             s.cursor     = "move";
17184             s.border     = "2px solid #aaa";
17185             s.zIndex     = 999;
17186
17187             // appendChild can blow up IE if invoked prior to the window load event
17188             // while rendering a table.  It is possible there are other scenarios
17189             // that would cause this to happen as well.
17190             body.insertBefore(div, body.firstChild);
17191         }
17192     },
17193
17194     /**
17195      * Initialization for the drag frame element.  Must be called in the
17196      * constructor of all subclasses
17197      * @method initFrame
17198      */
17199     initFrame: function() {
17200         this.createFrame();
17201     },
17202
17203     applyConfig: function() {
17204         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17205
17206         this.resizeFrame = (this.config.resizeFrame !== false);
17207         this.centerFrame = (this.config.centerFrame);
17208         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17209     },
17210
17211     /**
17212      * Resizes the drag frame to the dimensions of the clicked object, positions
17213      * it over the object, and finally displays it
17214      * @method showFrame
17215      * @param {int} iPageX X click position
17216      * @param {int} iPageY Y click position
17217      * @private
17218      */
17219     showFrame: function(iPageX, iPageY) {
17220         var el = this.getEl();
17221         var dragEl = this.getDragEl();
17222         var s = dragEl.style;
17223
17224         this._resizeProxy();
17225
17226         if (this.centerFrame) {
17227             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17228                            Math.round(parseInt(s.height, 10)/2) );
17229         }
17230
17231         this.setDragElPos(iPageX, iPageY);
17232
17233         Roo.fly(dragEl).show();
17234     },
17235
17236     /**
17237      * The proxy is automatically resized to the dimensions of the linked
17238      * element when a drag is initiated, unless resizeFrame is set to false
17239      * @method _resizeProxy
17240      * @private
17241      */
17242     _resizeProxy: function() {
17243         if (this.resizeFrame) {
17244             var el = this.getEl();
17245             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17246         }
17247     },
17248
17249     // overrides Roo.dd.DragDrop
17250     b4MouseDown: function(e) {
17251         var x = e.getPageX();
17252         var y = e.getPageY();
17253         this.autoOffset(x, y);
17254         this.setDragElPos(x, y);
17255     },
17256
17257     // overrides Roo.dd.DragDrop
17258     b4StartDrag: function(x, y) {
17259         // show the drag frame
17260         this.showFrame(x, y);
17261     },
17262
17263     // overrides Roo.dd.DragDrop
17264     b4EndDrag: function(e) {
17265         Roo.fly(this.getDragEl()).hide();
17266     },
17267
17268     // overrides Roo.dd.DragDrop
17269     // By default we try to move the element to the last location of the frame.
17270     // This is so that the default behavior mirrors that of Roo.dd.DD.
17271     endDrag: function(e) {
17272
17273         var lel = this.getEl();
17274         var del = this.getDragEl();
17275
17276         // Show the drag frame briefly so we can get its position
17277         del.style.visibility = "";
17278
17279         this.beforeMove();
17280         // Hide the linked element before the move to get around a Safari
17281         // rendering bug.
17282         lel.style.visibility = "hidden";
17283         Roo.dd.DDM.moveToEl(lel, del);
17284         del.style.visibility = "hidden";
17285         lel.style.visibility = "";
17286
17287         this.afterDrag();
17288     },
17289
17290     beforeMove : function(){
17291
17292     },
17293
17294     afterDrag : function(){
17295
17296     },
17297
17298     toString: function() {
17299         return ("DDProxy " + this.id);
17300     }
17301
17302 });
17303 /*
17304  * Based on:
17305  * Ext JS Library 1.1.1
17306  * Copyright(c) 2006-2007, Ext JS, LLC.
17307  *
17308  * Originally Released Under LGPL - original licence link has changed is not relivant.
17309  *
17310  * Fork - LGPL
17311  * <script type="text/javascript">
17312  */
17313
17314  /**
17315  * @class Roo.dd.DDTarget
17316  * A DragDrop implementation that does not move, but can be a drop
17317  * target.  You would get the same result by simply omitting implementation
17318  * for the event callbacks, but this way we reduce the processing cost of the
17319  * event listener and the callbacks.
17320  * @extends Roo.dd.DragDrop
17321  * @constructor
17322  * @param {String} id the id of the element that is a drop target
17323  * @param {String} sGroup the group of related DragDrop objects
17324  * @param {object} config an object containing configurable attributes
17325  *                 Valid properties for DDTarget in addition to those in
17326  *                 DragDrop:
17327  *                    none
17328  */
17329 Roo.dd.DDTarget = function(id, sGroup, config) {
17330     if (id) {
17331         this.initTarget(id, sGroup, config);
17332     }
17333 };
17334
17335 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17336 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17337     toString: function() {
17338         return ("DDTarget " + this.id);
17339     }
17340 });
17341 /*
17342  * Based on:
17343  * Ext JS Library 1.1.1
17344  * Copyright(c) 2006-2007, Ext JS, LLC.
17345  *
17346  * Originally Released Under LGPL - original licence link has changed is not relivant.
17347  *
17348  * Fork - LGPL
17349  * <script type="text/javascript">
17350  */
17351  
17352
17353 /**
17354  * @class Roo.dd.ScrollManager
17355  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17356  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17357  * @singleton
17358  */
17359 Roo.dd.ScrollManager = function(){
17360     var ddm = Roo.dd.DragDropMgr;
17361     var els = {};
17362     var dragEl = null;
17363     var proc = {};
17364     
17365     var onStop = function(e){
17366         dragEl = null;
17367         clearProc();
17368     };
17369     
17370     var triggerRefresh = function(){
17371         if(ddm.dragCurrent){
17372              ddm.refreshCache(ddm.dragCurrent.groups);
17373         }
17374     };
17375     
17376     var doScroll = function(){
17377         if(ddm.dragCurrent){
17378             var dds = Roo.dd.ScrollManager;
17379             if(!dds.animate){
17380                 if(proc.el.scroll(proc.dir, dds.increment)){
17381                     triggerRefresh();
17382                 }
17383             }else{
17384                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17385             }
17386         }
17387     };
17388     
17389     var clearProc = function(){
17390         if(proc.id){
17391             clearInterval(proc.id);
17392         }
17393         proc.id = 0;
17394         proc.el = null;
17395         proc.dir = "";
17396     };
17397     
17398     var startProc = function(el, dir){
17399         clearProc();
17400         proc.el = el;
17401         proc.dir = dir;
17402         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17403     };
17404     
17405     var onFire = function(e, isDrop){
17406         if(isDrop || !ddm.dragCurrent){ return; }
17407         var dds = Roo.dd.ScrollManager;
17408         if(!dragEl || dragEl != ddm.dragCurrent){
17409             dragEl = ddm.dragCurrent;
17410             // refresh regions on drag start
17411             dds.refreshCache();
17412         }
17413         
17414         var xy = Roo.lib.Event.getXY(e);
17415         var pt = new Roo.lib.Point(xy[0], xy[1]);
17416         for(var id in els){
17417             var el = els[id], r = el._region;
17418             if(r && r.contains(pt) && el.isScrollable()){
17419                 if(r.bottom - pt.y <= dds.thresh){
17420                     if(proc.el != el){
17421                         startProc(el, "down");
17422                     }
17423                     return;
17424                 }else if(r.right - pt.x <= dds.thresh){
17425                     if(proc.el != el){
17426                         startProc(el, "left");
17427                     }
17428                     return;
17429                 }else if(pt.y - r.top <= dds.thresh){
17430                     if(proc.el != el){
17431                         startProc(el, "up");
17432                     }
17433                     return;
17434                 }else if(pt.x - r.left <= dds.thresh){
17435                     if(proc.el != el){
17436                         startProc(el, "right");
17437                     }
17438                     return;
17439                 }
17440             }
17441         }
17442         clearProc();
17443     };
17444     
17445     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17446     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17447     
17448     return {
17449         /**
17450          * Registers new overflow element(s) to auto scroll
17451          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17452          */
17453         register : function(el){
17454             if(el instanceof Array){
17455                 for(var i = 0, len = el.length; i < len; i++) {
17456                         this.register(el[i]);
17457                 }
17458             }else{
17459                 el = Roo.get(el);
17460                 els[el.id] = el;
17461             }
17462         },
17463         
17464         /**
17465          * Unregisters overflow element(s) so they are no longer scrolled
17466          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17467          */
17468         unregister : function(el){
17469             if(el instanceof Array){
17470                 for(var i = 0, len = el.length; i < len; i++) {
17471                         this.unregister(el[i]);
17472                 }
17473             }else{
17474                 el = Roo.get(el);
17475                 delete els[el.id];
17476             }
17477         },
17478         
17479         /**
17480          * The number of pixels from the edge of a container the pointer needs to be to 
17481          * trigger scrolling (defaults to 25)
17482          * @type Number
17483          */
17484         thresh : 25,
17485         
17486         /**
17487          * The number of pixels to scroll in each scroll increment (defaults to 50)
17488          * @type Number
17489          */
17490         increment : 100,
17491         
17492         /**
17493          * The frequency of scrolls in milliseconds (defaults to 500)
17494          * @type Number
17495          */
17496         frequency : 500,
17497         
17498         /**
17499          * True to animate the scroll (defaults to true)
17500          * @type Boolean
17501          */
17502         animate: true,
17503         
17504         /**
17505          * The animation duration in seconds - 
17506          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17507          * @type Number
17508          */
17509         animDuration: .4,
17510         
17511         /**
17512          * Manually trigger a cache refresh.
17513          */
17514         refreshCache : function(){
17515             for(var id in els){
17516                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17517                     els[id]._region = els[id].getRegion();
17518                 }
17519             }
17520         }
17521     };
17522 }();/*
17523  * Based on:
17524  * Ext JS Library 1.1.1
17525  * Copyright(c) 2006-2007, Ext JS, LLC.
17526  *
17527  * Originally Released Under LGPL - original licence link has changed is not relivant.
17528  *
17529  * Fork - LGPL
17530  * <script type="text/javascript">
17531  */
17532  
17533
17534 /**
17535  * @class Roo.dd.Registry
17536  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17537  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17538  * @singleton
17539  */
17540 Roo.dd.Registry = function(){
17541     var elements = {}; 
17542     var handles = {}; 
17543     var autoIdSeed = 0;
17544
17545     var getId = function(el, autogen){
17546         if(typeof el == "string"){
17547             return el;
17548         }
17549         var id = el.id;
17550         if(!id && autogen !== false){
17551             id = "roodd-" + (++autoIdSeed);
17552             el.id = id;
17553         }
17554         return id;
17555     };
17556     
17557     return {
17558     /**
17559      * Register a drag drop element
17560      * @param {String|HTMLElement} element The id or DOM node to register
17561      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17562      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17563      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17564      * populated in the data object (if applicable):
17565      * <pre>
17566 Value      Description<br />
17567 ---------  ------------------------------------------<br />
17568 handles    Array of DOM nodes that trigger dragging<br />
17569            for the element being registered<br />
17570 isHandle   True if the element passed in triggers<br />
17571            dragging itself, else false
17572 </pre>
17573      */
17574         register : function(el, data){
17575             data = data || {};
17576             if(typeof el == "string"){
17577                 el = document.getElementById(el);
17578             }
17579             data.ddel = el;
17580             elements[getId(el)] = data;
17581             if(data.isHandle !== false){
17582                 handles[data.ddel.id] = data;
17583             }
17584             if(data.handles){
17585                 var hs = data.handles;
17586                 for(var i = 0, len = hs.length; i < len; i++){
17587                         handles[getId(hs[i])] = data;
17588                 }
17589             }
17590         },
17591
17592     /**
17593      * Unregister a drag drop element
17594      * @param {String|HTMLElement}  element The id or DOM node to unregister
17595      */
17596         unregister : function(el){
17597             var id = getId(el, false);
17598             var data = elements[id];
17599             if(data){
17600                 delete elements[id];
17601                 if(data.handles){
17602                     var hs = data.handles;
17603                     for(var i = 0, len = hs.length; i < len; i++){
17604                         delete handles[getId(hs[i], false)];
17605                     }
17606                 }
17607             }
17608         },
17609
17610     /**
17611      * Returns the handle registered for a DOM Node by id
17612      * @param {String|HTMLElement} id The DOM node or id to look up
17613      * @return {Object} handle The custom handle data
17614      */
17615         getHandle : function(id){
17616             if(typeof id != "string"){ // must be element?
17617                 id = id.id;
17618             }
17619             return handles[id];
17620         },
17621
17622     /**
17623      * Returns the handle that is registered for the DOM node that is the target of the event
17624      * @param {Event} e The event
17625      * @return {Object} handle The custom handle data
17626      */
17627         getHandleFromEvent : function(e){
17628             var t = Roo.lib.Event.getTarget(e);
17629             return t ? handles[t.id] : null;
17630         },
17631
17632     /**
17633      * Returns a custom data object that is registered for a DOM node by id
17634      * @param {String|HTMLElement} id The DOM node or id to look up
17635      * @return {Object} data The custom data
17636      */
17637         getTarget : function(id){
17638             if(typeof id != "string"){ // must be element?
17639                 id = id.id;
17640             }
17641             return elements[id];
17642         },
17643
17644     /**
17645      * Returns a custom data object that is registered for the DOM node that is the target of the event
17646      * @param {Event} e The event
17647      * @return {Object} data The custom data
17648      */
17649         getTargetFromEvent : function(e){
17650             var t = Roo.lib.Event.getTarget(e);
17651             return t ? elements[t.id] || handles[t.id] : null;
17652         }
17653     };
17654 }();/*
17655  * Based on:
17656  * Ext JS Library 1.1.1
17657  * Copyright(c) 2006-2007, Ext JS, LLC.
17658  *
17659  * Originally Released Under LGPL - original licence link has changed is not relivant.
17660  *
17661  * Fork - LGPL
17662  * <script type="text/javascript">
17663  */
17664  
17665
17666 /**
17667  * @class Roo.dd.StatusProxy
17668  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17669  * default drag proxy used by all Roo.dd components.
17670  * @constructor
17671  * @param {Object} config
17672  */
17673 Roo.dd.StatusProxy = function(config){
17674     Roo.apply(this, config);
17675     this.id = this.id || Roo.id();
17676     this.el = new Roo.Layer({
17677         dh: {
17678             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17679                 {tag: "div", cls: "x-dd-drop-icon"},
17680                 {tag: "div", cls: "x-dd-drag-ghost"}
17681             ]
17682         }, 
17683         shadow: !config || config.shadow !== false
17684     });
17685     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17686     this.dropStatus = this.dropNotAllowed;
17687 };
17688
17689 Roo.dd.StatusProxy.prototype = {
17690     /**
17691      * @cfg {String} dropAllowed
17692      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17693      */
17694     dropAllowed : "x-dd-drop-ok",
17695     /**
17696      * @cfg {String} dropNotAllowed
17697      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17698      */
17699     dropNotAllowed : "x-dd-drop-nodrop",
17700
17701     /**
17702      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17703      * over the current target element.
17704      * @param {String} cssClass The css class for the new drop status indicator image
17705      */
17706     setStatus : function(cssClass){
17707         cssClass = cssClass || this.dropNotAllowed;
17708         if(this.dropStatus != cssClass){
17709             this.el.replaceClass(this.dropStatus, cssClass);
17710             this.dropStatus = cssClass;
17711         }
17712     },
17713
17714     /**
17715      * Resets the status indicator to the default dropNotAllowed value
17716      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17717      */
17718     reset : function(clearGhost){
17719         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17720         this.dropStatus = this.dropNotAllowed;
17721         if(clearGhost){
17722             this.ghost.update("");
17723         }
17724     },
17725
17726     /**
17727      * Updates the contents of the ghost element
17728      * @param {String} html The html that will replace the current innerHTML of the ghost element
17729      */
17730     update : function(html){
17731         if(typeof html == "string"){
17732             this.ghost.update(html);
17733         }else{
17734             this.ghost.update("");
17735             html.style.margin = "0";
17736             this.ghost.dom.appendChild(html);
17737         }
17738         // ensure float = none set?? cant remember why though.
17739         var el = this.ghost.dom.firstChild;
17740                 if(el){
17741                         Roo.fly(el).setStyle('float', 'none');
17742                 }
17743     },
17744     
17745     /**
17746      * Returns the underlying proxy {@link Roo.Layer}
17747      * @return {Roo.Layer} el
17748     */
17749     getEl : function(){
17750         return this.el;
17751     },
17752
17753     /**
17754      * Returns the ghost element
17755      * @return {Roo.Element} el
17756      */
17757     getGhost : function(){
17758         return this.ghost;
17759     },
17760
17761     /**
17762      * Hides the proxy
17763      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17764      */
17765     hide : function(clear){
17766         this.el.hide();
17767         if(clear){
17768             this.reset(true);
17769         }
17770     },
17771
17772     /**
17773      * Stops the repair animation if it's currently running
17774      */
17775     stop : function(){
17776         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17777             this.anim.stop();
17778         }
17779     },
17780
17781     /**
17782      * Displays this proxy
17783      */
17784     show : function(){
17785         this.el.show();
17786     },
17787
17788     /**
17789      * Force the Layer to sync its shadow and shim positions to the element
17790      */
17791     sync : function(){
17792         this.el.sync();
17793     },
17794
17795     /**
17796      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17797      * invalid drop operation by the item being dragged.
17798      * @param {Array} xy The XY position of the element ([x, y])
17799      * @param {Function} callback The function to call after the repair is complete
17800      * @param {Object} scope The scope in which to execute the callback
17801      */
17802     repair : function(xy, callback, scope){
17803         this.callback = callback;
17804         this.scope = scope;
17805         if(xy && this.animRepair !== false){
17806             this.el.addClass("x-dd-drag-repair");
17807             this.el.hideUnders(true);
17808             this.anim = this.el.shift({
17809                 duration: this.repairDuration || .5,
17810                 easing: 'easeOut',
17811                 xy: xy,
17812                 stopFx: true,
17813                 callback: this.afterRepair,
17814                 scope: this
17815             });
17816         }else{
17817             this.afterRepair();
17818         }
17819     },
17820
17821     // private
17822     afterRepair : function(){
17823         this.hide(true);
17824         if(typeof this.callback == "function"){
17825             this.callback.call(this.scope || this);
17826         }
17827         this.callback = null;
17828         this.scope = null;
17829     }
17830 };/*
17831  * Based on:
17832  * Ext JS Library 1.1.1
17833  * Copyright(c) 2006-2007, Ext JS, LLC.
17834  *
17835  * Originally Released Under LGPL - original licence link has changed is not relivant.
17836  *
17837  * Fork - LGPL
17838  * <script type="text/javascript">
17839  */
17840
17841 /**
17842  * @class Roo.dd.DragSource
17843  * @extends Roo.dd.DDProxy
17844  * A simple class that provides the basic implementation needed to make any element draggable.
17845  * @constructor
17846  * @param {String/HTMLElement/Element} el The container element
17847  * @param {Object} config
17848  */
17849 Roo.dd.DragSource = function(el, config){
17850     this.el = Roo.get(el);
17851     this.dragData = {};
17852     
17853     Roo.apply(this, config);
17854     
17855     if(!this.proxy){
17856         this.proxy = new Roo.dd.StatusProxy();
17857     }
17858
17859     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17860           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17861     
17862     this.dragging = false;
17863 };
17864
17865 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17866     /**
17867      * @cfg {String} dropAllowed
17868      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17869      */
17870     dropAllowed : "x-dd-drop-ok",
17871     /**
17872      * @cfg {String} dropNotAllowed
17873      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17874      */
17875     dropNotAllowed : "x-dd-drop-nodrop",
17876
17877     /**
17878      * Returns the data object associated with this drag source
17879      * @return {Object} data An object containing arbitrary data
17880      */
17881     getDragData : function(e){
17882         return this.dragData;
17883     },
17884
17885     // private
17886     onDragEnter : function(e, id){
17887         var target = Roo.dd.DragDropMgr.getDDById(id);
17888         this.cachedTarget = target;
17889         if(this.beforeDragEnter(target, e, id) !== false){
17890             if(target.isNotifyTarget){
17891                 var status = target.notifyEnter(this, e, this.dragData);
17892                 this.proxy.setStatus(status);
17893             }else{
17894                 this.proxy.setStatus(this.dropAllowed);
17895             }
17896             
17897             if(this.afterDragEnter){
17898                 /**
17899                  * An empty function by default, but provided so that you can perform a custom action
17900                  * when the dragged item enters the drop target by providing an implementation.
17901                  * @param {Roo.dd.DragDrop} target The drop target
17902                  * @param {Event} e The event object
17903                  * @param {String} id The id of the dragged element
17904                  * @method afterDragEnter
17905                  */
17906                 this.afterDragEnter(target, e, id);
17907             }
17908         }
17909     },
17910
17911     /**
17912      * An empty function by default, but provided so that you can perform a custom action
17913      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17914      * @param {Roo.dd.DragDrop} target The drop target
17915      * @param {Event} e The event object
17916      * @param {String} id The id of the dragged element
17917      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17918      */
17919     beforeDragEnter : function(target, e, id){
17920         return true;
17921     },
17922
17923     // private
17924     alignElWithMouse: function() {
17925         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17926         this.proxy.sync();
17927     },
17928
17929     // private
17930     onDragOver : function(e, id){
17931         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17932         if(this.beforeDragOver(target, e, id) !== false){
17933             if(target.isNotifyTarget){
17934                 var status = target.notifyOver(this, e, this.dragData);
17935                 this.proxy.setStatus(status);
17936             }
17937
17938             if(this.afterDragOver){
17939                 /**
17940                  * An empty function by default, but provided so that you can perform a custom action
17941                  * while the dragged item is over the drop target by providing an implementation.
17942                  * @param {Roo.dd.DragDrop} target The drop target
17943                  * @param {Event} e The event object
17944                  * @param {String} id The id of the dragged element
17945                  * @method afterDragOver
17946                  */
17947                 this.afterDragOver(target, e, id);
17948             }
17949         }
17950     },
17951
17952     /**
17953      * An empty function by default, but provided so that you can perform a custom action
17954      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17955      * @param {Roo.dd.DragDrop} target The drop target
17956      * @param {Event} e The event object
17957      * @param {String} id The id of the dragged element
17958      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17959      */
17960     beforeDragOver : function(target, e, id){
17961         return true;
17962     },
17963
17964     // private
17965     onDragOut : function(e, id){
17966         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17967         if(this.beforeDragOut(target, e, id) !== false){
17968             if(target.isNotifyTarget){
17969                 target.notifyOut(this, e, this.dragData);
17970             }
17971             this.proxy.reset();
17972             if(this.afterDragOut){
17973                 /**
17974                  * An empty function by default, but provided so that you can perform a custom action
17975                  * after the dragged item is dragged out of the target without dropping.
17976                  * @param {Roo.dd.DragDrop} target The drop target
17977                  * @param {Event} e The event object
17978                  * @param {String} id The id of the dragged element
17979                  * @method afterDragOut
17980                  */
17981                 this.afterDragOut(target, e, id);
17982             }
17983         }
17984         this.cachedTarget = null;
17985     },
17986
17987     /**
17988      * An empty function by default, but provided so that you can perform a custom action before the dragged
17989      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17990      * @param {Roo.dd.DragDrop} target The drop target
17991      * @param {Event} e The event object
17992      * @param {String} id The id of the dragged element
17993      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17994      */
17995     beforeDragOut : function(target, e, id){
17996         return true;
17997     },
17998     
17999     // private
18000     onDragDrop : function(e, id){
18001         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18002         if(this.beforeDragDrop(target, e, id) !== false){
18003             if(target.isNotifyTarget){
18004                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18005                     this.onValidDrop(target, e, id);
18006                 }else{
18007                     this.onInvalidDrop(target, e, id);
18008                 }
18009             }else{
18010                 this.onValidDrop(target, e, id);
18011             }
18012             
18013             if(this.afterDragDrop){
18014                 /**
18015                  * An empty function by default, but provided so that you can perform a custom action
18016                  * after a valid drag drop has occurred by providing an implementation.
18017                  * @param {Roo.dd.DragDrop} target The drop target
18018                  * @param {Event} e The event object
18019                  * @param {String} id The id of the dropped element
18020                  * @method afterDragDrop
18021                  */
18022                 this.afterDragDrop(target, e, id);
18023             }
18024         }
18025         delete this.cachedTarget;
18026     },
18027
18028     /**
18029      * An empty function by default, but provided so that you can perform a custom action before the dragged
18030      * item is dropped onto the target and optionally cancel the onDragDrop.
18031      * @param {Roo.dd.DragDrop} target The drop target
18032      * @param {Event} e The event object
18033      * @param {String} id The id of the dragged element
18034      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18035      */
18036     beforeDragDrop : function(target, e, id){
18037         return true;
18038     },
18039
18040     // private
18041     onValidDrop : function(target, e, id){
18042         this.hideProxy();
18043         if(this.afterValidDrop){
18044             /**
18045              * An empty function by default, but provided so that you can perform a custom action
18046              * after a valid drop has occurred by providing an implementation.
18047              * @param {Object} target The target DD 
18048              * @param {Event} e The event object
18049              * @param {String} id The id of the dropped element
18050              * @method afterInvalidDrop
18051              */
18052             this.afterValidDrop(target, e, id);
18053         }
18054     },
18055
18056     // private
18057     getRepairXY : function(e, data){
18058         return this.el.getXY();  
18059     },
18060
18061     // private
18062     onInvalidDrop : function(target, e, id){
18063         this.beforeInvalidDrop(target, e, id);
18064         if(this.cachedTarget){
18065             if(this.cachedTarget.isNotifyTarget){
18066                 this.cachedTarget.notifyOut(this, e, this.dragData);
18067             }
18068             this.cacheTarget = null;
18069         }
18070         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18071
18072         if(this.afterInvalidDrop){
18073             /**
18074              * An empty function by default, but provided so that you can perform a custom action
18075              * after an invalid drop has occurred by providing an implementation.
18076              * @param {Event} e The event object
18077              * @param {String} id The id of the dropped element
18078              * @method afterInvalidDrop
18079              */
18080             this.afterInvalidDrop(e, id);
18081         }
18082     },
18083
18084     // private
18085     afterRepair : function(){
18086         if(Roo.enableFx){
18087             this.el.highlight(this.hlColor || "c3daf9");
18088         }
18089         this.dragging = false;
18090     },
18091
18092     /**
18093      * An empty function by default, but provided so that you can perform a custom action after an invalid
18094      * drop has occurred.
18095      * @param {Roo.dd.DragDrop} target The drop target
18096      * @param {Event} e The event object
18097      * @param {String} id The id of the dragged element
18098      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18099      */
18100     beforeInvalidDrop : function(target, e, id){
18101         return true;
18102     },
18103
18104     // private
18105     handleMouseDown : function(e){
18106         if(this.dragging) {
18107             return;
18108         }
18109         var data = this.getDragData(e);
18110         if(data && this.onBeforeDrag(data, e) !== false){
18111             this.dragData = data;
18112             this.proxy.stop();
18113             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18114         } 
18115     },
18116
18117     /**
18118      * An empty function by default, but provided so that you can perform a custom action before the initial
18119      * drag event begins and optionally cancel it.
18120      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18121      * @param {Event} e The event object
18122      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18123      */
18124     onBeforeDrag : function(data, e){
18125         return true;
18126     },
18127
18128     /**
18129      * An empty function by default, but provided so that you can perform a custom action once the initial
18130      * drag event has begun.  The drag cannot be canceled from this function.
18131      * @param {Number} x The x position of the click on the dragged object
18132      * @param {Number} y The y position of the click on the dragged object
18133      */
18134     onStartDrag : Roo.emptyFn,
18135
18136     // private - YUI override
18137     startDrag : function(x, y){
18138         this.proxy.reset();
18139         this.dragging = true;
18140         this.proxy.update("");
18141         this.onInitDrag(x, y);
18142         this.proxy.show();
18143     },
18144
18145     // private
18146     onInitDrag : function(x, y){
18147         var clone = this.el.dom.cloneNode(true);
18148         clone.id = Roo.id(); // prevent duplicate ids
18149         this.proxy.update(clone);
18150         this.onStartDrag(x, y);
18151         return true;
18152     },
18153
18154     /**
18155      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18156      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18157      */
18158     getProxy : function(){
18159         return this.proxy;  
18160     },
18161
18162     /**
18163      * Hides the drag source's {@link Roo.dd.StatusProxy}
18164      */
18165     hideProxy : function(){
18166         this.proxy.hide();  
18167         this.proxy.reset(true);
18168         this.dragging = false;
18169     },
18170
18171     // private
18172     triggerCacheRefresh : function(){
18173         Roo.dd.DDM.refreshCache(this.groups);
18174     },
18175
18176     // private - override to prevent hiding
18177     b4EndDrag: function(e) {
18178     },
18179
18180     // private - override to prevent moving
18181     endDrag : function(e){
18182         this.onEndDrag(this.dragData, e);
18183     },
18184
18185     // private
18186     onEndDrag : function(data, e){
18187     },
18188     
18189     // private - pin to cursor
18190     autoOffset : function(x, y) {
18191         this.setDelta(-12, -20);
18192     }    
18193 });/*
18194  * Based on:
18195  * Ext JS Library 1.1.1
18196  * Copyright(c) 2006-2007, Ext JS, LLC.
18197  *
18198  * Originally Released Under LGPL - original licence link has changed is not relivant.
18199  *
18200  * Fork - LGPL
18201  * <script type="text/javascript">
18202  */
18203
18204
18205 /**
18206  * @class Roo.dd.DropTarget
18207  * @extends Roo.dd.DDTarget
18208  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18209  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18210  * @constructor
18211  * @param {String/HTMLElement/Element} el The container element
18212  * @param {Object} config
18213  */
18214 Roo.dd.DropTarget = function(el, config){
18215     this.el = Roo.get(el);
18216     
18217     Roo.apply(this, config);
18218     
18219     if(this.containerScroll){
18220         Roo.dd.ScrollManager.register(this.el);
18221     }
18222     
18223     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18224           {isTarget: true});
18225
18226 };
18227
18228 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18229     /**
18230      * @cfg {String} overClass
18231      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18232      */
18233     /**
18234      * @cfg {String} dropAllowed
18235      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18236      */
18237     dropAllowed : "x-dd-drop-ok",
18238     /**
18239      * @cfg {String} dropNotAllowed
18240      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18241      */
18242     dropNotAllowed : "x-dd-drop-nodrop",
18243
18244     // private
18245     isTarget : true,
18246
18247     // private
18248     isNotifyTarget : true,
18249
18250     /**
18251      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18252      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18253      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18255      * @param {Event} e The event
18256      * @param {Object} data An object containing arbitrary data supplied by the drag source
18257      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18258      * underlying {@link Roo.dd.StatusProxy} can be updated
18259      */
18260     notifyEnter : function(dd, e, data){
18261         if(this.overClass){
18262             this.el.addClass(this.overClass);
18263         }
18264         return this.dropAllowed;
18265     },
18266
18267     /**
18268      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18269      * This method will be called on every mouse movement while the drag source is over the drop target.
18270      * This default implementation simply returns the dropAllowed config value.
18271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18272      * @param {Event} e The event
18273      * @param {Object} data An object containing arbitrary data supplied by the drag source
18274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18275      * underlying {@link Roo.dd.StatusProxy} can be updated
18276      */
18277     notifyOver : function(dd, e, data){
18278         return this.dropAllowed;
18279     },
18280
18281     /**
18282      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18283      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18284      * overClass (if any) from the drop element.
18285      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18286      * @param {Event} e The event
18287      * @param {Object} data An object containing arbitrary data supplied by the drag source
18288      */
18289     notifyOut : function(dd, e, data){
18290         if(this.overClass){
18291             this.el.removeClass(this.overClass);
18292         }
18293     },
18294
18295     /**
18296      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18297      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18298      * implementation that does something to process the drop event and returns true so that the drag source's
18299      * repair action does not run.
18300      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18301      * @param {Event} e The event
18302      * @param {Object} data An object containing arbitrary data supplied by the drag source
18303      * @return {Boolean} True if the drop was valid, else false
18304      */
18305     notifyDrop : function(dd, e, data){
18306         return false;
18307     }
18308 });/*
18309  * Based on:
18310  * Ext JS Library 1.1.1
18311  * Copyright(c) 2006-2007, Ext JS, LLC.
18312  *
18313  * Originally Released Under LGPL - original licence link has changed is not relivant.
18314  *
18315  * Fork - LGPL
18316  * <script type="text/javascript">
18317  */
18318
18319
18320 /**
18321  * @class Roo.dd.DragZone
18322  * @extends Roo.dd.DragSource
18323  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18324  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18325  * @constructor
18326  * @param {String/HTMLElement/Element} el The container element
18327  * @param {Object} config
18328  */
18329 Roo.dd.DragZone = function(el, config){
18330     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18331     if(this.containerScroll){
18332         Roo.dd.ScrollManager.register(this.el);
18333     }
18334 };
18335
18336 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18337     /**
18338      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18339      * for auto scrolling during drag operations.
18340      */
18341     /**
18342      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18343      * method after a failed drop (defaults to "c3daf9" - light blue)
18344      */
18345
18346     /**
18347      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18348      * for a valid target to drag based on the mouse down. Override this method
18349      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18350      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18351      * @param {EventObject} e The mouse down event
18352      * @return {Object} The dragData
18353      */
18354     getDragData : function(e){
18355         return Roo.dd.Registry.getHandleFromEvent(e);
18356     },
18357     
18358     /**
18359      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18360      * this.dragData.ddel
18361      * @param {Number} x The x position of the click on the dragged object
18362      * @param {Number} y The y position of the click on the dragged object
18363      * @return {Boolean} true to continue the drag, false to cancel
18364      */
18365     onInitDrag : function(x, y){
18366         this.proxy.update(this.dragData.ddel.cloneNode(true));
18367         this.onStartDrag(x, y);
18368         return true;
18369     },
18370     
18371     /**
18372      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18373      */
18374     afterRepair : function(){
18375         if(Roo.enableFx){
18376             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18377         }
18378         this.dragging = false;
18379     },
18380
18381     /**
18382      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18383      * the XY of this.dragData.ddel
18384      * @param {EventObject} e The mouse up event
18385      * @return {Array} The xy location (e.g. [100, 200])
18386      */
18387     getRepairXY : function(e){
18388         return Roo.Element.fly(this.dragData.ddel).getXY();  
18389     }
18390 });/*
18391  * Based on:
18392  * Ext JS Library 1.1.1
18393  * Copyright(c) 2006-2007, Ext JS, LLC.
18394  *
18395  * Originally Released Under LGPL - original licence link has changed is not relivant.
18396  *
18397  * Fork - LGPL
18398  * <script type="text/javascript">
18399  */
18400 /**
18401  * @class Roo.dd.DropZone
18402  * @extends Roo.dd.DropTarget
18403  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18404  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18405  * @constructor
18406  * @param {String/HTMLElement/Element} el The container element
18407  * @param {Object} config
18408  */
18409 Roo.dd.DropZone = function(el, config){
18410     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18411 };
18412
18413 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18414     /**
18415      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18416      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18417      * provide your own custom lookup.
18418      * @param {Event} e The event
18419      * @return {Object} data The custom data
18420      */
18421     getTargetFromEvent : function(e){
18422         return Roo.dd.Registry.getTargetFromEvent(e);
18423     },
18424
18425     /**
18426      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18427      * that it has registered.  This method has no default implementation and should be overridden to provide
18428      * node-specific processing if necessary.
18429      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18430      * {@link #getTargetFromEvent} for this node)
18431      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18432      * @param {Event} e The event
18433      * @param {Object} data An object containing arbitrary data supplied by the drag source
18434      */
18435     onNodeEnter : function(n, dd, e, data){
18436         
18437     },
18438
18439     /**
18440      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18441      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18442      * overridden to provide the proper feedback.
18443      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18444      * {@link #getTargetFromEvent} for this node)
18445      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18446      * @param {Event} e The event
18447      * @param {Object} data An object containing arbitrary data supplied by the drag source
18448      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18449      * underlying {@link Roo.dd.StatusProxy} can be updated
18450      */
18451     onNodeOver : function(n, dd, e, data){
18452         return this.dropAllowed;
18453     },
18454
18455     /**
18456      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18457      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18458      * node-specific processing if necessary.
18459      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18460      * {@link #getTargetFromEvent} for this node)
18461      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18462      * @param {Event} e The event
18463      * @param {Object} data An object containing arbitrary data supplied by the drag source
18464      */
18465     onNodeOut : function(n, dd, e, data){
18466         
18467     },
18468
18469     /**
18470      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18471      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18472      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18473      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18474      * {@link #getTargetFromEvent} for this node)
18475      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18476      * @param {Event} e The event
18477      * @param {Object} data An object containing arbitrary data supplied by the drag source
18478      * @return {Boolean} True if the drop was valid, else false
18479      */
18480     onNodeDrop : function(n, dd, e, data){
18481         return false;
18482     },
18483
18484     /**
18485      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18486      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18487      * it should be overridden to provide the proper feedback if necessary.
18488      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18489      * @param {Event} e The event
18490      * @param {Object} data An object containing arbitrary data supplied by the drag source
18491      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18492      * underlying {@link Roo.dd.StatusProxy} can be updated
18493      */
18494     onContainerOver : function(dd, e, data){
18495         return this.dropNotAllowed;
18496     },
18497
18498     /**
18499      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18500      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18501      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18502      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18503      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18504      * @param {Event} e The event
18505      * @param {Object} data An object containing arbitrary data supplied by the drag source
18506      * @return {Boolean} True if the drop was valid, else false
18507      */
18508     onContainerDrop : function(dd, e, data){
18509         return false;
18510     },
18511
18512     /**
18513      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18514      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18515      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18516      * you should override this method and provide a custom implementation.
18517      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18518      * @param {Event} e The event
18519      * @param {Object} data An object containing arbitrary data supplied by the drag source
18520      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18521      * underlying {@link Roo.dd.StatusProxy} can be updated
18522      */
18523     notifyEnter : function(dd, e, data){
18524         return this.dropNotAllowed;
18525     },
18526
18527     /**
18528      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18529      * This method will be called on every mouse movement while the drag source is over the drop zone.
18530      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18531      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18532      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18533      * registered node, it will call {@link #onContainerOver}.
18534      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18535      * @param {Event} e The event
18536      * @param {Object} data An object containing arbitrary data supplied by the drag source
18537      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18538      * underlying {@link Roo.dd.StatusProxy} can be updated
18539      */
18540     notifyOver : function(dd, e, data){
18541         var n = this.getTargetFromEvent(e);
18542         if(!n){ // not over valid drop target
18543             if(this.lastOverNode){
18544                 this.onNodeOut(this.lastOverNode, dd, e, data);
18545                 this.lastOverNode = null;
18546             }
18547             return this.onContainerOver(dd, e, data);
18548         }
18549         if(this.lastOverNode != n){
18550             if(this.lastOverNode){
18551                 this.onNodeOut(this.lastOverNode, dd, e, data);
18552             }
18553             this.onNodeEnter(n, dd, e, data);
18554             this.lastOverNode = n;
18555         }
18556         return this.onNodeOver(n, dd, e, data);
18557     },
18558
18559     /**
18560      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18561      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18562      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18563      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18564      * @param {Event} e The event
18565      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18566      */
18567     notifyOut : function(dd, e, data){
18568         if(this.lastOverNode){
18569             this.onNodeOut(this.lastOverNode, dd, e, data);
18570             this.lastOverNode = null;
18571         }
18572     },
18573
18574     /**
18575      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18576      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18577      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18578      * otherwise it will call {@link #onContainerDrop}.
18579      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18580      * @param {Event} e The event
18581      * @param {Object} data An object containing arbitrary data supplied by the drag source
18582      * @return {Boolean} True if the drop was valid, else false
18583      */
18584     notifyDrop : function(dd, e, data){
18585         if(this.lastOverNode){
18586             this.onNodeOut(this.lastOverNode, dd, e, data);
18587             this.lastOverNode = null;
18588         }
18589         var n = this.getTargetFromEvent(e);
18590         return n ?
18591             this.onNodeDrop(n, dd, e, data) :
18592             this.onContainerDrop(dd, e, data);
18593     },
18594
18595     // private
18596     triggerCacheRefresh : function(){
18597         Roo.dd.DDM.refreshCache(this.groups);
18598     }  
18599 });/*
18600  * Based on:
18601  * Ext JS Library 1.1.1
18602  * Copyright(c) 2006-2007, Ext JS, LLC.
18603  *
18604  * Originally Released Under LGPL - original licence link has changed is not relivant.
18605  *
18606  * Fork - LGPL
18607  * <script type="text/javascript">
18608  */
18609
18610
18611 /**
18612  * @class Roo.data.SortTypes
18613  * @singleton
18614  * Defines the default sorting (casting?) comparison functions used when sorting data.
18615  */
18616 Roo.data.SortTypes = {
18617     /**
18618      * Default sort that does nothing
18619      * @param {Mixed} s The value being converted
18620      * @return {Mixed} The comparison value
18621      */
18622     none : function(s){
18623         return s;
18624     },
18625     
18626     /**
18627      * The regular expression used to strip tags
18628      * @type {RegExp}
18629      * @property
18630      */
18631     stripTagsRE : /<\/?[^>]+>/gi,
18632     
18633     /**
18634      * Strips all HTML tags to sort on text only
18635      * @param {Mixed} s The value being converted
18636      * @return {String} The comparison value
18637      */
18638     asText : function(s){
18639         return String(s).replace(this.stripTagsRE, "");
18640     },
18641     
18642     /**
18643      * Strips all HTML tags to sort on text only - Case insensitive
18644      * @param {Mixed} s The value being converted
18645      * @return {String} The comparison value
18646      */
18647     asUCText : function(s){
18648         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18649     },
18650     
18651     /**
18652      * Case insensitive string
18653      * @param {Mixed} s The value being converted
18654      * @return {String} The comparison value
18655      */
18656     asUCString : function(s) {
18657         return String(s).toUpperCase();
18658     },
18659     
18660     /**
18661      * Date sorting
18662      * @param {Mixed} s The value being converted
18663      * @return {Number} The comparison value
18664      */
18665     asDate : function(s) {
18666         if(!s){
18667             return 0;
18668         }
18669         if(s instanceof Date){
18670             return s.getTime();
18671         }
18672         return Date.parse(String(s));
18673     },
18674     
18675     /**
18676      * Float sorting
18677      * @param {Mixed} s The value being converted
18678      * @return {Float} The comparison value
18679      */
18680     asFloat : function(s) {
18681         var val = parseFloat(String(s).replace(/,/g, ""));
18682         if(isNaN(val)) val = 0;
18683         return val;
18684     },
18685     
18686     /**
18687      * Integer sorting
18688      * @param {Mixed} s The value being converted
18689      * @return {Number} The comparison value
18690      */
18691     asInt : function(s) {
18692         var val = parseInt(String(s).replace(/,/g, ""));
18693         if(isNaN(val)) val = 0;
18694         return val;
18695     }
18696 };/*
18697  * Based on:
18698  * Ext JS Library 1.1.1
18699  * Copyright(c) 2006-2007, Ext JS, LLC.
18700  *
18701  * Originally Released Under LGPL - original licence link has changed is not relivant.
18702  *
18703  * Fork - LGPL
18704  * <script type="text/javascript">
18705  */
18706
18707 /**
18708 * @class Roo.data.Record
18709  * Instances of this class encapsulate both record <em>definition</em> information, and record
18710  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18711  * to access Records cached in an {@link Roo.data.Store} object.<br>
18712  * <p>
18713  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18714  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18715  * objects.<br>
18716  * <p>
18717  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18718  * @constructor
18719  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18720  * {@link #create}. The parameters are the same.
18721  * @param {Array} data An associative Array of data values keyed by the field name.
18722  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18723  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18724  * not specified an integer id is generated.
18725  */
18726 Roo.data.Record = function(data, id){
18727     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18728     this.data = data;
18729 };
18730
18731 /**
18732  * Generate a constructor for a specific record layout.
18733  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18734  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18735  * Each field definition object may contain the following properties: <ul>
18736  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18737  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18738  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18739  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18740  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18741  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18742  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18743  * this may be omitted.</p></li>
18744  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18745  * <ul><li>auto (Default, implies no conversion)</li>
18746  * <li>string</li>
18747  * <li>int</li>
18748  * <li>float</li>
18749  * <li>boolean</li>
18750  * <li>date</li></ul></p></li>
18751  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18752  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18753  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18754  * by the Reader into an object that will be stored in the Record. It is passed the
18755  * following parameters:<ul>
18756  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18757  * </ul></p></li>
18758  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18759  * </ul>
18760  * <br>usage:<br><pre><code>
18761 var TopicRecord = Roo.data.Record.create(
18762     {name: 'title', mapping: 'topic_title'},
18763     {name: 'author', mapping: 'username'},
18764     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18765     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18766     {name: 'lastPoster', mapping: 'user2'},
18767     {name: 'excerpt', mapping: 'post_text'}
18768 );
18769
18770 var myNewRecord = new TopicRecord({
18771     title: 'Do my job please',
18772     author: 'noobie',
18773     totalPosts: 1,
18774     lastPost: new Date(),
18775     lastPoster: 'Animal',
18776     excerpt: 'No way dude!'
18777 });
18778 myStore.add(myNewRecord);
18779 </code></pre>
18780  * @method create
18781  * @static
18782  */
18783 Roo.data.Record.create = function(o){
18784     var f = function(){
18785         f.superclass.constructor.apply(this, arguments);
18786     };
18787     Roo.extend(f, Roo.data.Record);
18788     var p = f.prototype;
18789     p.fields = new Roo.util.MixedCollection(false, function(field){
18790         return field.name;
18791     });
18792     for(var i = 0, len = o.length; i < len; i++){
18793         p.fields.add(new Roo.data.Field(o[i]));
18794     }
18795     f.getField = function(name){
18796         return p.fields.get(name);  
18797     };
18798     return f;
18799 };
18800
18801 Roo.data.Record.AUTO_ID = 1000;
18802 Roo.data.Record.EDIT = 'edit';
18803 Roo.data.Record.REJECT = 'reject';
18804 Roo.data.Record.COMMIT = 'commit';
18805
18806 Roo.data.Record.prototype = {
18807     /**
18808      * Readonly flag - true if this record has been modified.
18809      * @type Boolean
18810      */
18811     dirty : false,
18812     editing : false,
18813     error: null,
18814     modified: null,
18815
18816     // private
18817     join : function(store){
18818         this.store = store;
18819     },
18820
18821     /**
18822      * Set the named field to the specified value.
18823      * @param {String} name The name of the field to set.
18824      * @param {Object} value The value to set the field to.
18825      */
18826     set : function(name, value){
18827         if(this.data[name] == value){
18828             return;
18829         }
18830         this.dirty = true;
18831         if(!this.modified){
18832             this.modified = {};
18833         }
18834         if(typeof this.modified[name] == 'undefined'){
18835             this.modified[name] = this.data[name];
18836         }
18837         this.data[name] = value;
18838         if(!this.editing){
18839             this.store.afterEdit(this);
18840         }       
18841     },
18842
18843     /**
18844      * Get the value of the named field.
18845      * @param {String} name The name of the field to get the value of.
18846      * @return {Object} The value of the field.
18847      */
18848     get : function(name){
18849         return this.data[name]; 
18850     },
18851
18852     // private
18853     beginEdit : function(){
18854         this.editing = true;
18855         this.modified = {}; 
18856     },
18857
18858     // private
18859     cancelEdit : function(){
18860         this.editing = false;
18861         delete this.modified;
18862     },
18863
18864     // private
18865     endEdit : function(){
18866         this.editing = false;
18867         if(this.dirty && this.store){
18868             this.store.afterEdit(this);
18869         }
18870     },
18871
18872     /**
18873      * Usually called by the {@link Roo.data.Store} which owns the Record.
18874      * Rejects all changes made to the Record since either creation, or the last commit operation.
18875      * Modified fields are reverted to their original values.
18876      * <p>
18877      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18878      * of reject operations.
18879      */
18880     reject : function(){
18881         var m = this.modified;
18882         for(var n in m){
18883             if(typeof m[n] != "function"){
18884                 this.data[n] = m[n];
18885             }
18886         }
18887         this.dirty = false;
18888         delete this.modified;
18889         this.editing = false;
18890         if(this.store){
18891             this.store.afterReject(this);
18892         }
18893     },
18894
18895     /**
18896      * Usually called by the {@link Roo.data.Store} which owns the Record.
18897      * Commits all changes made to the Record since either creation, or the last commit operation.
18898      * <p>
18899      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18900      * of commit operations.
18901      */
18902     commit : function(){
18903         this.dirty = false;
18904         delete this.modified;
18905         this.editing = false;
18906         if(this.store){
18907             this.store.afterCommit(this);
18908         }
18909     },
18910
18911     // private
18912     hasError : function(){
18913         return this.error != null;
18914     },
18915
18916     // private
18917     clearError : function(){
18918         this.error = null;
18919     },
18920
18921     /**
18922      * Creates a copy of this record.
18923      * @param {String} id (optional) A new record id if you don't want to use this record's id
18924      * @return {Record}
18925      */
18926     copy : function(newId) {
18927         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18928     }
18929 };/*
18930  * Based on:
18931  * Ext JS Library 1.1.1
18932  * Copyright(c) 2006-2007, Ext JS, LLC.
18933  *
18934  * Originally Released Under LGPL - original licence link has changed is not relivant.
18935  *
18936  * Fork - LGPL
18937  * <script type="text/javascript">
18938  */
18939
18940
18941
18942 /**
18943  * @class Roo.data.Store
18944  * @extends Roo.util.Observable
18945  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18946  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18947  * <p>
18948  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
18949  * has no knowledge of the format of the data returned by the Proxy.<br>
18950  * <p>
18951  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18952  * instances from the data object. These records are cached and made available through accessor functions.
18953  * @constructor
18954  * Creates a new Store.
18955  * @param {Object} config A config object containing the objects needed for the Store to access data,
18956  * and read the data into Records.
18957  */
18958 Roo.data.Store = function(config){
18959     this.data = new Roo.util.MixedCollection(false);
18960     this.data.getKey = function(o){
18961         return o.id;
18962     };
18963     this.baseParams = {};
18964     // private
18965     this.paramNames = {
18966         "start" : "start",
18967         "limit" : "limit",
18968         "sort" : "sort",
18969         "dir" : "dir"
18970     };
18971
18972     if(config && config.data){
18973         this.inlineData = config.data;
18974         delete config.data;
18975     }
18976
18977     Roo.apply(this, config);
18978     
18979     if(this.reader){ // reader passed
18980         this.reader = Roo.factory(this.reader, Roo.data);
18981         this.reader.xmodule = this.xmodule || false;
18982         if(!this.recordType){
18983             this.recordType = this.reader.recordType;
18984         }
18985         if(this.reader.onMetaChange){
18986             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18987         }
18988     }
18989
18990     if(this.recordType){
18991         this.fields = this.recordType.prototype.fields;
18992     }
18993     this.modified = [];
18994
18995     this.addEvents({
18996         /**
18997          * @event datachanged
18998          * Fires when the data cache has changed, and a widget which is using this Store
18999          * as a Record cache should refresh its view.
19000          * @param {Store} this
19001          */
19002         datachanged : true,
19003         /**
19004          * @event metachange
19005          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19006          * @param {Store} this
19007          * @param {Object} meta The JSON metadata
19008          */
19009         metachange : true,
19010         /**
19011          * @event add
19012          * Fires when Records have been added to the Store
19013          * @param {Store} this
19014          * @param {Roo.data.Record[]} records The array of Records added
19015          * @param {Number} index The index at which the record(s) were added
19016          */
19017         add : true,
19018         /**
19019          * @event remove
19020          * Fires when a Record has been removed from the Store
19021          * @param {Store} this
19022          * @param {Roo.data.Record} record The Record that was removed
19023          * @param {Number} index The index at which the record was removed
19024          */
19025         remove : true,
19026         /**
19027          * @event update
19028          * Fires when a Record has been updated
19029          * @param {Store} this
19030          * @param {Roo.data.Record} record The Record that was updated
19031          * @param {String} operation The update operation being performed.  Value may be one of:
19032          * <pre><code>
19033  Roo.data.Record.EDIT
19034  Roo.data.Record.REJECT
19035  Roo.data.Record.COMMIT
19036          * </code></pre>
19037          */
19038         update : true,
19039         /**
19040          * @event clear
19041          * Fires when the data cache has been cleared.
19042          * @param {Store} this
19043          */
19044         clear : true,
19045         /**
19046          * @event beforeload
19047          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19048          * the load action will be canceled.
19049          * @param {Store} this
19050          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19051          */
19052         beforeload : true,
19053         /**
19054          * @event load
19055          * Fires after a new set of Records has been loaded.
19056          * @param {Store} this
19057          * @param {Roo.data.Record[]} records The Records that were loaded
19058          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19059          */
19060         load : true,
19061         /**
19062          * @event loadexception
19063          * Fires if an exception occurs in the Proxy during loading.
19064          * Called with the signature of the Proxy's "loadexception" event.
19065          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19066          * 
19067          * @param {Proxy} 
19068          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19069          * @param {Object} load options 
19070          * @param {Object} jsonData from your request (normally this contains the Exception)
19071          */
19072         loadexception : true
19073     });
19074     
19075     if(this.proxy){
19076         this.proxy = Roo.factory(this.proxy, Roo.data);
19077         this.proxy.xmodule = this.xmodule || false;
19078         this.relayEvents(this.proxy,  ["loadexception"]);
19079     }
19080     this.sortToggle = {};
19081
19082     Roo.data.Store.superclass.constructor.call(this);
19083
19084     if(this.inlineData){
19085         this.loadData(this.inlineData);
19086         delete this.inlineData;
19087     }
19088 };
19089 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19090      /**
19091     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19092     * without a remote query - used by combo/forms at present.
19093     */
19094     
19095     /**
19096     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19097     */
19098     /**
19099     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19100     */
19101     /**
19102     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19103     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19104     */
19105     /**
19106     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19107     * on any HTTP request
19108     */
19109     /**
19110     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19111     */
19112     /**
19113     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19114     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19115     */
19116     remoteSort : false,
19117
19118     /**
19119     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19120      * loaded or when a record is removed. (defaults to false).
19121     */
19122     pruneModifiedRecords : false,
19123
19124     // private
19125     lastOptions : null,
19126
19127     /**
19128      * Add Records to the Store and fires the add event.
19129      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19130      */
19131     add : function(records){
19132         records = [].concat(records);
19133         for(var i = 0, len = records.length; i < len; i++){
19134             records[i].join(this);
19135         }
19136         var index = this.data.length;
19137         this.data.addAll(records);
19138         this.fireEvent("add", this, records, index);
19139     },
19140
19141     /**
19142      * Remove a Record from the Store and fires the remove event.
19143      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19144      */
19145     remove : function(record){
19146         var index = this.data.indexOf(record);
19147         this.data.removeAt(index);
19148         if(this.pruneModifiedRecords){
19149             this.modified.remove(record);
19150         }
19151         this.fireEvent("remove", this, record, index);
19152     },
19153
19154     /**
19155      * Remove all Records from the Store and fires the clear event.
19156      */
19157     removeAll : function(){
19158         this.data.clear();
19159         if(this.pruneModifiedRecords){
19160             this.modified = [];
19161         }
19162         this.fireEvent("clear", this);
19163     },
19164
19165     /**
19166      * Inserts Records to the Store at the given index and fires the add event.
19167      * @param {Number} index The start index at which to insert the passed Records.
19168      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19169      */
19170     insert : function(index, records){
19171         records = [].concat(records);
19172         for(var i = 0, len = records.length; i < len; i++){
19173             this.data.insert(index, records[i]);
19174             records[i].join(this);
19175         }
19176         this.fireEvent("add", this, records, index);
19177     },
19178
19179     /**
19180      * Get the index within the cache of the passed Record.
19181      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19182      * @return {Number} The index of the passed Record. Returns -1 if not found.
19183      */
19184     indexOf : function(record){
19185         return this.data.indexOf(record);
19186     },
19187
19188     /**
19189      * Get the index within the cache of the Record with the passed id.
19190      * @param {String} id The id of the Record to find.
19191      * @return {Number} The index of the Record. Returns -1 if not found.
19192      */
19193     indexOfId : function(id){
19194         return this.data.indexOfKey(id);
19195     },
19196
19197     /**
19198      * Get the Record with the specified id.
19199      * @param {String} id The id of the Record to find.
19200      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19201      */
19202     getById : function(id){
19203         return this.data.key(id);
19204     },
19205
19206     /**
19207      * Get the Record at the specified index.
19208      * @param {Number} index The index of the Record to find.
19209      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19210      */
19211     getAt : function(index){
19212         return this.data.itemAt(index);
19213     },
19214
19215     /**
19216      * Returns a range of Records between specified indices.
19217      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19218      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19219      * @return {Roo.data.Record[]} An array of Records
19220      */
19221     getRange : function(start, end){
19222         return this.data.getRange(start, end);
19223     },
19224
19225     // private
19226     storeOptions : function(o){
19227         o = Roo.apply({}, o);
19228         delete o.callback;
19229         delete o.scope;
19230         this.lastOptions = o;
19231     },
19232
19233     /**
19234      * Loads the Record cache from the configured Proxy using the configured Reader.
19235      * <p>
19236      * If using remote paging, then the first load call must specify the <em>start</em>
19237      * and <em>limit</em> properties in the options.params property to establish the initial
19238      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19239      * <p>
19240      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19241      * and this call will return before the new data has been loaded. Perform any post-processing
19242      * in a callback function, or in a "load" event handler.</strong>
19243      * <p>
19244      * @param {Object} options An object containing properties which control loading options:<ul>
19245      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19246      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19247      * passed the following arguments:<ul>
19248      * <li>r : Roo.data.Record[]</li>
19249      * <li>options: Options object from the load call</li>
19250      * <li>success: Boolean success indicator</li></ul></li>
19251      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19252      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19253      * </ul>
19254      */
19255     load : function(options){
19256         options = options || {};
19257         if(this.fireEvent("beforeload", this, options) !== false){
19258             this.storeOptions(options);
19259             var p = Roo.apply(options.params || {}, this.baseParams);
19260             if(this.sortInfo && this.remoteSort){
19261                 var pn = this.paramNames;
19262                 p[pn["sort"]] = this.sortInfo.field;
19263                 p[pn["dir"]] = this.sortInfo.direction;
19264             }
19265             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19266         }
19267     },
19268
19269     /**
19270      * Reloads the Record cache from the configured Proxy using the configured Reader and
19271      * the options from the last load operation performed.
19272      * @param {Object} options (optional) An object containing properties which may override the options
19273      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19274      * the most recently used options are reused).
19275      */
19276     reload : function(options){
19277         this.load(Roo.applyIf(options||{}, this.lastOptions));
19278     },
19279
19280     // private
19281     // Called as a callback by the Reader during a load operation.
19282     loadRecords : function(o, options, success){
19283         if(!o || success === false){
19284             if(success !== false){
19285                 this.fireEvent("load", this, [], options);
19286             }
19287             if(options.callback){
19288                 options.callback.call(options.scope || this, [], options, false);
19289             }
19290             return;
19291         }
19292         // if data returned failure - throw an exception.
19293         if (o.success === false) {
19294             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19295             return;
19296         }
19297         var r = o.records, t = o.totalRecords || r.length;
19298         if(!options || options.add !== true){
19299             if(this.pruneModifiedRecords){
19300                 this.modified = [];
19301             }
19302             for(var i = 0, len = r.length; i < len; i++){
19303                 r[i].join(this);
19304             }
19305             if(this.snapshot){
19306                 this.data = this.snapshot;
19307                 delete this.snapshot;
19308             }
19309             this.data.clear();
19310             this.data.addAll(r);
19311             this.totalLength = t;
19312             this.applySort();
19313             this.fireEvent("datachanged", this);
19314         }else{
19315             this.totalLength = Math.max(t, this.data.length+r.length);
19316             this.add(r);
19317         }
19318         this.fireEvent("load", this, r, options);
19319         if(options.callback){
19320             options.callback.call(options.scope || this, r, options, true);
19321         }
19322     },
19323
19324     /**
19325      * Loads data from a passed data block. A Reader which understands the format of the data
19326      * must have been configured in the constructor.
19327      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19328      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19329      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19330      */
19331     loadData : function(o, append){
19332         var r = this.reader.readRecords(o);
19333         this.loadRecords(r, {add: append}, true);
19334     },
19335
19336     /**
19337      * Gets the number of cached records.
19338      * <p>
19339      * <em>If using paging, this may not be the total size of the dataset. If the data object
19340      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19341      * the data set size</em>
19342      */
19343     getCount : function(){
19344         return this.data.length || 0;
19345     },
19346
19347     /**
19348      * Gets the total number of records in the dataset as returned by the server.
19349      * <p>
19350      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19351      * the dataset size</em>
19352      */
19353     getTotalCount : function(){
19354         return this.totalLength || 0;
19355     },
19356
19357     /**
19358      * Returns the sort state of the Store as an object with two properties:
19359      * <pre><code>
19360  field {String} The name of the field by which the Records are sorted
19361  direction {String} The sort order, "ASC" or "DESC"
19362      * </code></pre>
19363      */
19364     getSortState : function(){
19365         return this.sortInfo;
19366     },
19367
19368     // private
19369     applySort : function(){
19370         if(this.sortInfo && !this.remoteSort){
19371             var s = this.sortInfo, f = s.field;
19372             var st = this.fields.get(f).sortType;
19373             var fn = function(r1, r2){
19374                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19375                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19376             };
19377             this.data.sort(s.direction, fn);
19378             if(this.snapshot && this.snapshot != this.data){
19379                 this.snapshot.sort(s.direction, fn);
19380             }
19381         }
19382     },
19383
19384     /**
19385      * Sets the default sort column and order to be used by the next load operation.
19386      * @param {String} fieldName The name of the field to sort by.
19387      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19388      */
19389     setDefaultSort : function(field, dir){
19390         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19391     },
19392
19393     /**
19394      * Sort the Records.
19395      * If remote sorting is used, the sort is performed on the server, and the cache is
19396      * reloaded. If local sorting is used, the cache is sorted internally.
19397      * @param {String} fieldName The name of the field to sort by.
19398      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19399      */
19400     sort : function(fieldName, dir){
19401         var f = this.fields.get(fieldName);
19402         if(!dir){
19403             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19404                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19405             }else{
19406                 dir = f.sortDir;
19407             }
19408         }
19409         this.sortToggle[f.name] = dir;
19410         this.sortInfo = {field: f.name, direction: dir};
19411         if(!this.remoteSort){
19412             this.applySort();
19413             this.fireEvent("datachanged", this);
19414         }else{
19415             this.load(this.lastOptions);
19416         }
19417     },
19418
19419     /**
19420      * Calls the specified function for each of the Records in the cache.
19421      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19422      * Returning <em>false</em> aborts and exits the iteration.
19423      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19424      */
19425     each : function(fn, scope){
19426         this.data.each(fn, scope);
19427     },
19428
19429     /**
19430      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19431      * (e.g., during paging).
19432      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19433      */
19434     getModifiedRecords : function(){
19435         return this.modified;
19436     },
19437
19438     // private
19439     createFilterFn : function(property, value, anyMatch){
19440         if(!value.exec){ // not a regex
19441             value = String(value);
19442             if(value.length == 0){
19443                 return false;
19444             }
19445             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19446         }
19447         return function(r){
19448             return value.test(r.data[property]);
19449         };
19450     },
19451
19452     /**
19453      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19454      * @param {String} property A field on your records
19455      * @param {Number} start The record index to start at (defaults to 0)
19456      * @param {Number} end The last record index to include (defaults to length - 1)
19457      * @return {Number} The sum
19458      */
19459     sum : function(property, start, end){
19460         var rs = this.data.items, v = 0;
19461         start = start || 0;
19462         end = (end || end === 0) ? end : rs.length-1;
19463
19464         for(var i = start; i <= end; i++){
19465             v += (rs[i].data[property] || 0);
19466         }
19467         return v;
19468     },
19469
19470     /**
19471      * Filter the records by a specified property.
19472      * @param {String} field A field on your records
19473      * @param {String/RegExp} value Either a string that the field
19474      * should start with or a RegExp to test against the field
19475      * @param {Boolean} anyMatch True to match any part not just the beginning
19476      */
19477     filter : function(property, value, anyMatch){
19478         var fn = this.createFilterFn(property, value, anyMatch);
19479         return fn ? this.filterBy(fn) : this.clearFilter();
19480     },
19481
19482     /**
19483      * Filter by a function. The specified function will be called with each
19484      * record in this data source. If the function returns true the record is included,
19485      * otherwise it is filtered.
19486      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19487      * @param {Object} scope (optional) The scope of the function (defaults to this)
19488      */
19489     filterBy : function(fn, scope){
19490         this.snapshot = this.snapshot || this.data;
19491         this.data = this.queryBy(fn, scope||this);
19492         this.fireEvent("datachanged", this);
19493     },
19494
19495     /**
19496      * Query the records by a specified property.
19497      * @param {String} field A field on your records
19498      * @param {String/RegExp} value Either a string that the field
19499      * should start with or a RegExp to test against the field
19500      * @param {Boolean} anyMatch True to match any part not just the beginning
19501      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19502      */
19503     query : function(property, value, anyMatch){
19504         var fn = this.createFilterFn(property, value, anyMatch);
19505         return fn ? this.queryBy(fn) : this.data.clone();
19506     },
19507
19508     /**
19509      * Query by a function. The specified function will be called with each
19510      * record in this data source. If the function returns true the record is included
19511      * in the results.
19512      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19513      * @param {Object} scope (optional) The scope of the function (defaults to this)
19514       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19515      **/
19516     queryBy : function(fn, scope){
19517         var data = this.snapshot || this.data;
19518         return data.filterBy(fn, scope||this);
19519     },
19520
19521     /**
19522      * Collects unique values for a particular dataIndex from this store.
19523      * @param {String} dataIndex The property to collect
19524      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19525      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19526      * @return {Array} An array of the unique values
19527      **/
19528     collect : function(dataIndex, allowNull, bypassFilter){
19529         var d = (bypassFilter === true && this.snapshot) ?
19530                 this.snapshot.items : this.data.items;
19531         var v, sv, r = [], l = {};
19532         for(var i = 0, len = d.length; i < len; i++){
19533             v = d[i].data[dataIndex];
19534             sv = String(v);
19535             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19536                 l[sv] = true;
19537                 r[r.length] = v;
19538             }
19539         }
19540         return r;
19541     },
19542
19543     /**
19544      * Revert to a view of the Record cache with no filtering applied.
19545      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19546      */
19547     clearFilter : function(suppressEvent){
19548         if(this.snapshot && this.snapshot != this.data){
19549             this.data = this.snapshot;
19550             delete this.snapshot;
19551             if(suppressEvent !== true){
19552                 this.fireEvent("datachanged", this);
19553             }
19554         }
19555     },
19556
19557     // private
19558     afterEdit : function(record){
19559         if(this.modified.indexOf(record) == -1){
19560             this.modified.push(record);
19561         }
19562         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19563     },
19564
19565     // private
19566     afterReject : function(record){
19567         this.modified.remove(record);
19568         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19569     },
19570
19571     // private
19572     afterCommit : function(record){
19573         this.modified.remove(record);
19574         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19575     },
19576
19577     /**
19578      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19579      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19580      */
19581     commitChanges : function(){
19582         var m = this.modified.slice(0);
19583         this.modified = [];
19584         for(var i = 0, len = m.length; i < len; i++){
19585             m[i].commit();
19586         }
19587     },
19588
19589     /**
19590      * Cancel outstanding changes on all changed records.
19591      */
19592     rejectChanges : function(){
19593         var m = this.modified.slice(0);
19594         this.modified = [];
19595         for(var i = 0, len = m.length; i < len; i++){
19596             m[i].reject();
19597         }
19598     },
19599
19600     onMetaChange : function(meta, rtype, o){
19601         this.recordType = rtype;
19602         this.fields = rtype.prototype.fields;
19603         delete this.snapshot;
19604         this.sortInfo = meta.sortInfo;
19605         this.modified = [];
19606         this.fireEvent('metachange', this, this.reader.meta);
19607     }
19608 });/*
19609  * Based on:
19610  * Ext JS Library 1.1.1
19611  * Copyright(c) 2006-2007, Ext JS, LLC.
19612  *
19613  * Originally Released Under LGPL - original licence link has changed is not relivant.
19614  *
19615  * Fork - LGPL
19616  * <script type="text/javascript">
19617  */
19618
19619 /**
19620  * @class Roo.data.SimpleStore
19621  * @extends Roo.data.Store
19622  * Small helper class to make creating Stores from Array data easier.
19623  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19624  * @cfg {Array} fields An array of field definition objects, or field name strings.
19625  * @cfg {Array} data The multi-dimensional array of data
19626  * @constructor
19627  * @param {Object} config
19628  */
19629 Roo.data.SimpleStore = function(config){
19630     Roo.data.SimpleStore.superclass.constructor.call(this, {
19631         isLocal : true,
19632         reader: new Roo.data.ArrayReader({
19633                 id: config.id
19634             },
19635             Roo.data.Record.create(config.fields)
19636         ),
19637         proxy : new Roo.data.MemoryProxy(config.data)
19638     });
19639     this.load();
19640 };
19641 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19642  * Based on:
19643  * Ext JS Library 1.1.1
19644  * Copyright(c) 2006-2007, Ext JS, LLC.
19645  *
19646  * Originally Released Under LGPL - original licence link has changed is not relivant.
19647  *
19648  * Fork - LGPL
19649  * <script type="text/javascript">
19650  */
19651
19652 /**
19653 /**
19654  * @extends Roo.data.Store
19655  * @class Roo.data.JsonStore
19656  * Small helper class to make creating Stores for JSON data easier. <br/>
19657 <pre><code>
19658 var store = new Roo.data.JsonStore({
19659     url: 'get-images.php',
19660     root: 'images',
19661     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19662 });
19663 </code></pre>
19664  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19665  * JsonReader and HttpProxy (unless inline data is provided).</b>
19666  * @cfg {Array} fields An array of field definition objects, or field name strings.
19667  * @constructor
19668  * @param {Object} config
19669  */
19670 Roo.data.JsonStore = function(c){
19671     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19672         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19673         reader: new Roo.data.JsonReader(c, c.fields)
19674     }));
19675 };
19676 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19677  * Based on:
19678  * Ext JS Library 1.1.1
19679  * Copyright(c) 2006-2007, Ext JS, LLC.
19680  *
19681  * Originally Released Under LGPL - original licence link has changed is not relivant.
19682  *
19683  * Fork - LGPL
19684  * <script type="text/javascript">
19685  */
19686
19687  
19688 Roo.data.Field = function(config){
19689     if(typeof config == "string"){
19690         config = {name: config};
19691     }
19692     Roo.apply(this, config);
19693     
19694     if(!this.type){
19695         this.type = "auto";
19696     }
19697     
19698     var st = Roo.data.SortTypes;
19699     // named sortTypes are supported, here we look them up
19700     if(typeof this.sortType == "string"){
19701         this.sortType = st[this.sortType];
19702     }
19703     
19704     // set default sortType for strings and dates
19705     if(!this.sortType){
19706         switch(this.type){
19707             case "string":
19708                 this.sortType = st.asUCString;
19709                 break;
19710             case "date":
19711                 this.sortType = st.asDate;
19712                 break;
19713             default:
19714                 this.sortType = st.none;
19715         }
19716     }
19717
19718     // define once
19719     var stripRe = /[\$,%]/g;
19720
19721     // prebuilt conversion function for this field, instead of
19722     // switching every time we're reading a value
19723     if(!this.convert){
19724         var cv, dateFormat = this.dateFormat;
19725         switch(this.type){
19726             case "":
19727             case "auto":
19728             case undefined:
19729                 cv = function(v){ return v; };
19730                 break;
19731             case "string":
19732                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19733                 break;
19734             case "int":
19735                 cv = function(v){
19736                     return v !== undefined && v !== null && v !== '' ?
19737                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19738                     };
19739                 break;
19740             case "float":
19741                 cv = function(v){
19742                     return v !== undefined && v !== null && v !== '' ?
19743                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19744                     };
19745                 break;
19746             case "bool":
19747             case "boolean":
19748                 cv = function(v){ return v === true || v === "true" || v == 1; };
19749                 break;
19750             case "date":
19751                 cv = function(v){
19752                     if(!v){
19753                         return '';
19754                     }
19755                     if(v instanceof Date){
19756                         return v;
19757                     }
19758                     if(dateFormat){
19759                         if(dateFormat == "timestamp"){
19760                             return new Date(v*1000);
19761                         }
19762                         return Date.parseDate(v, dateFormat);
19763                     }
19764                     var parsed = Date.parse(v);
19765                     return parsed ? new Date(parsed) : null;
19766                 };
19767              break;
19768             
19769         }
19770         this.convert = cv;
19771     }
19772 };
19773
19774 Roo.data.Field.prototype = {
19775     dateFormat: null,
19776     defaultValue: "",
19777     mapping: null,
19778     sortType : null,
19779     sortDir : "ASC"
19780 };/*
19781  * Based on:
19782  * Ext JS Library 1.1.1
19783  * Copyright(c) 2006-2007, Ext JS, LLC.
19784  *
19785  * Originally Released Under LGPL - original licence link has changed is not relivant.
19786  *
19787  * Fork - LGPL
19788  * <script type="text/javascript">
19789  */
19790  
19791 // Base class for reading structured data from a data source.  This class is intended to be
19792 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19793
19794 /**
19795  * @class Roo.data.DataReader
19796  * Base class for reading structured data from a data source.  This class is intended to be
19797  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19798  */
19799
19800 Roo.data.DataReader = function(meta, recordType){
19801     
19802     this.meta = meta;
19803     
19804     this.recordType = recordType instanceof Array ? 
19805         Roo.data.Record.create(recordType) : recordType;
19806 };
19807
19808 Roo.data.DataReader.prototype = {
19809      /**
19810      * Create an empty record
19811      * @param {Object} data (optional) - overlay some values
19812      * @return {Roo.data.Record} record created.
19813      */
19814     newRow :  function(d) {
19815         var da =  {};
19816         this.recordType.prototype.fields.each(function(c) {
19817             switch( c.type) {
19818                 case 'int' : da[c.name] = 0; break;
19819                 case 'date' : da[c.name] = new Date(); break;
19820                 case 'float' : da[c.name] = 0.0; break;
19821                 case 'boolean' : da[c.name] = false; break;
19822                 default : da[c.name] = ""; break;
19823             }
19824             
19825         });
19826         return new this.recordType(Roo.apply(da, d));
19827     }
19828     
19829 };/*
19830  * Based on:
19831  * Ext JS Library 1.1.1
19832  * Copyright(c) 2006-2007, Ext JS, LLC.
19833  *
19834  * Originally Released Under LGPL - original licence link has changed is not relivant.
19835  *
19836  * Fork - LGPL
19837  * <script type="text/javascript">
19838  */
19839
19840 /**
19841  * @class Roo.data.DataProxy
19842  * @extends Roo.data.Observable
19843  * This class is an abstract base class for implementations which provide retrieval of
19844  * unformatted data objects.<br>
19845  * <p>
19846  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19847  * (of the appropriate type which knows how to parse the data object) to provide a block of
19848  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19849  * <p>
19850  * Custom implementations must implement the load method as described in
19851  * {@link Roo.data.HttpProxy#load}.
19852  */
19853 Roo.data.DataProxy = function(){
19854     this.addEvents({
19855         /**
19856          * @event beforeload
19857          * Fires before a network request is made to retrieve a data object.
19858          * @param {Object} This DataProxy object.
19859          * @param {Object} params The params parameter to the load function.
19860          */
19861         beforeload : true,
19862         /**
19863          * @event load
19864          * Fires before the load method's callback is called.
19865          * @param {Object} This DataProxy object.
19866          * @param {Object} o The data object.
19867          * @param {Object} arg The callback argument object passed to the load function.
19868          */
19869         load : true,
19870         /**
19871          * @event loadexception
19872          * Fires if an Exception occurs during data retrieval.
19873          * @param {Object} This DataProxy object.
19874          * @param {Object} o The data object.
19875          * @param {Object} arg The callback argument object passed to the load function.
19876          * @param {Object} e The Exception.
19877          */
19878         loadexception : true
19879     });
19880     Roo.data.DataProxy.superclass.constructor.call(this);
19881 };
19882
19883 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19884
19885     /**
19886      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19887      */
19888 /*
19889  * Based on:
19890  * Ext JS Library 1.1.1
19891  * Copyright(c) 2006-2007, Ext JS, LLC.
19892  *
19893  * Originally Released Under LGPL - original licence link has changed is not relivant.
19894  *
19895  * Fork - LGPL
19896  * <script type="text/javascript">
19897  */
19898 /**
19899  * @class Roo.data.MemoryProxy
19900  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19901  * to the Reader when its load method is called.
19902  * @constructor
19903  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19904  */
19905 Roo.data.MemoryProxy = function(data){
19906     if (data.data) {
19907         data = data.data;
19908     }
19909     Roo.data.MemoryProxy.superclass.constructor.call(this);
19910     this.data = data;
19911 };
19912
19913 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19914     /**
19915      * Load data from the requested source (in this case an in-memory
19916      * data object passed to the constructor), read the data object into
19917      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19918      * process that block using the passed callback.
19919      * @param {Object} params This parameter is not used by the MemoryProxy class.
19920      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19921      * object into a block of Roo.data.Records.
19922      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19923      * The function must be passed <ul>
19924      * <li>The Record block object</li>
19925      * <li>The "arg" argument from the load function</li>
19926      * <li>A boolean success indicator</li>
19927      * </ul>
19928      * @param {Object} scope The scope in which to call the callback
19929      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19930      */
19931     load : function(params, reader, callback, scope, arg){
19932         params = params || {};
19933         var result;
19934         try {
19935             result = reader.readRecords(this.data);
19936         }catch(e){
19937             this.fireEvent("loadexception", this, arg, null, e);
19938             callback.call(scope, null, arg, false);
19939             return;
19940         }
19941         callback.call(scope, result, arg, true);
19942     },
19943     
19944     // private
19945     update : function(params, records){
19946         
19947     }
19948 });/*
19949  * Based on:
19950  * Ext JS Library 1.1.1
19951  * Copyright(c) 2006-2007, Ext JS, LLC.
19952  *
19953  * Originally Released Under LGPL - original licence link has changed is not relivant.
19954  *
19955  * Fork - LGPL
19956  * <script type="text/javascript">
19957  */
19958 /**
19959  * @class Roo.data.HttpProxy
19960  * @extends Roo.data.DataProxy
19961  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19962  * configured to reference a certain URL.<br><br>
19963  * <p>
19964  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19965  * from which the running page was served.<br><br>
19966  * <p>
19967  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19968  * <p>
19969  * Be aware that to enable the browser to parse an XML document, the server must set
19970  * the Content-Type header in the HTTP response to "text/xml".
19971  * @constructor
19972  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19973  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19974  * will be used to make the request.
19975  */
19976 Roo.data.HttpProxy = function(conn){
19977     Roo.data.HttpProxy.superclass.constructor.call(this);
19978     // is conn a conn config or a real conn?
19979     this.conn = conn;
19980     this.useAjax = !conn || !conn.events;
19981   
19982 };
19983
19984 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19985     // thse are take from connection...
19986     
19987     /**
19988      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19989      */
19990     /**
19991      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19992      * extra parameters to each request made by this object. (defaults to undefined)
19993      */
19994     /**
19995      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19996      *  to each request made by this object. (defaults to undefined)
19997      */
19998     /**
19999      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20000      */
20001     /**
20002      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20003      */
20004      /**
20005      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20006      * @type Boolean
20007      */
20008   
20009
20010     /**
20011      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20012      * @type Boolean
20013      */
20014     /**
20015      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20016      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20017      * a finer-grained basis than the DataProxy events.
20018      */
20019     getConnection : function(){
20020         return this.useAjax ? Roo.Ajax : this.conn;
20021     },
20022
20023     /**
20024      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20025      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20026      * process that block using the passed callback.
20027      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20028      * for the request to the remote server.
20029      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20030      * object into a block of Roo.data.Records.
20031      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20032      * The function must be passed <ul>
20033      * <li>The Record block object</li>
20034      * <li>The "arg" argument from the load function</li>
20035      * <li>A boolean success indicator</li>
20036      * </ul>
20037      * @param {Object} scope The scope in which to call the callback
20038      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20039      */
20040     load : function(params, reader, callback, scope, arg){
20041         if(this.fireEvent("beforeload", this, params) !== false){
20042             var  o = {
20043                 params : params || {},
20044                 request: {
20045                     callback : callback,
20046                     scope : scope,
20047                     arg : arg
20048                 },
20049                 reader: reader,
20050                 callback : this.loadResponse,
20051                 scope: this
20052             };
20053             if(this.useAjax){
20054                 Roo.applyIf(o, this.conn);
20055                 if(this.activeRequest){
20056                     Roo.Ajax.abort(this.activeRequest);
20057                 }
20058                 this.activeRequest = Roo.Ajax.request(o);
20059             }else{
20060                 this.conn.request(o);
20061             }
20062         }else{
20063             callback.call(scope||this, null, arg, false);
20064         }
20065     },
20066
20067     // private
20068     loadResponse : function(o, success, response){
20069         delete this.activeRequest;
20070         if(!success){
20071             this.fireEvent("loadexception", this, o, response);
20072             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20073             return;
20074         }
20075         var result;
20076         try {
20077             result = o.reader.read(response);
20078         }catch(e){
20079             this.fireEvent("loadexception", this, o, response, e);
20080             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20081             return;
20082         }
20083         
20084         this.fireEvent("load", this, o, o.request.arg);
20085         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20086     },
20087
20088     // private
20089     update : function(dataSet){
20090
20091     },
20092
20093     // private
20094     updateResponse : function(dataSet){
20095
20096     }
20097 });/*
20098  * Based on:
20099  * Ext JS Library 1.1.1
20100  * Copyright(c) 2006-2007, Ext JS, LLC.
20101  *
20102  * Originally Released Under LGPL - original licence link has changed is not relivant.
20103  *
20104  * Fork - LGPL
20105  * <script type="text/javascript">
20106  */
20107
20108 /**
20109  * @class Roo.data.ScriptTagProxy
20110  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20111  * other than the originating domain of the running page.<br><br>
20112  * <p>
20113  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20114  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20115  * <p>
20116  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20117  * source code that is used as the source inside a &lt;script> tag.<br><br>
20118  * <p>
20119  * In order for the browser to process the returned data, the server must wrap the data object
20120  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20121  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20122  * depending on whether the callback name was passed:
20123  * <p>
20124  * <pre><code>
20125 boolean scriptTag = false;
20126 String cb = request.getParameter("callback");
20127 if (cb != null) {
20128     scriptTag = true;
20129     response.setContentType("text/javascript");
20130 } else {
20131     response.setContentType("application/x-json");
20132 }
20133 Writer out = response.getWriter();
20134 if (scriptTag) {
20135     out.write(cb + "(");
20136 }
20137 out.print(dataBlock.toJsonString());
20138 if (scriptTag) {
20139     out.write(");");
20140 }
20141 </pre></code>
20142  *
20143  * @constructor
20144  * @param {Object} config A configuration object.
20145  */
20146 Roo.data.ScriptTagProxy = function(config){
20147     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20148     Roo.apply(this, config);
20149     this.head = document.getElementsByTagName("head")[0];
20150 };
20151
20152 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20153
20154 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20155     /**
20156      * @cfg {String} url The URL from which to request the data object.
20157      */
20158     /**
20159      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20160      */
20161     timeout : 30000,
20162     /**
20163      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20164      * the server the name of the callback function set up by the load call to process the returned data object.
20165      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20166      * javascript output which calls this named function passing the data object as its only parameter.
20167      */
20168     callbackParam : "callback",
20169     /**
20170      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20171      * name to the request.
20172      */
20173     nocache : true,
20174
20175     /**
20176      * Load data from the configured URL, read the data object into
20177      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20178      * process that block using the passed callback.
20179      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20180      * for the request to the remote server.
20181      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20182      * object into a block of Roo.data.Records.
20183      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20184      * The function must be passed <ul>
20185      * <li>The Record block object</li>
20186      * <li>The "arg" argument from the load function</li>
20187      * <li>A boolean success indicator</li>
20188      * </ul>
20189      * @param {Object} scope The scope in which to call the callback
20190      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20191      */
20192     load : function(params, reader, callback, scope, arg){
20193         if(this.fireEvent("beforeload", this, params) !== false){
20194
20195             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20196
20197             var url = this.url;
20198             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20199             if(this.nocache){
20200                 url += "&_dc=" + (new Date().getTime());
20201             }
20202             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20203             var trans = {
20204                 id : transId,
20205                 cb : "stcCallback"+transId,
20206                 scriptId : "stcScript"+transId,
20207                 params : params,
20208                 arg : arg,
20209                 url : url,
20210                 callback : callback,
20211                 scope : scope,
20212                 reader : reader
20213             };
20214             var conn = this;
20215
20216             window[trans.cb] = function(o){
20217                 conn.handleResponse(o, trans);
20218             };
20219
20220             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20221
20222             if(this.autoAbort !== false){
20223                 this.abort();
20224             }
20225
20226             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20227
20228             var script = document.createElement("script");
20229             script.setAttribute("src", url);
20230             script.setAttribute("type", "text/javascript");
20231             script.setAttribute("id", trans.scriptId);
20232             this.head.appendChild(script);
20233
20234             this.trans = trans;
20235         }else{
20236             callback.call(scope||this, null, arg, false);
20237         }
20238     },
20239
20240     // private
20241     isLoading : function(){
20242         return this.trans ? true : false;
20243     },
20244
20245     /**
20246      * Abort the current server request.
20247      */
20248     abort : function(){
20249         if(this.isLoading()){
20250             this.destroyTrans(this.trans);
20251         }
20252     },
20253
20254     // private
20255     destroyTrans : function(trans, isLoaded){
20256         this.head.removeChild(document.getElementById(trans.scriptId));
20257         clearTimeout(trans.timeoutId);
20258         if(isLoaded){
20259             window[trans.cb] = undefined;
20260             try{
20261                 delete window[trans.cb];
20262             }catch(e){}
20263         }else{
20264             // if hasn't been loaded, wait for load to remove it to prevent script error
20265             window[trans.cb] = function(){
20266                 window[trans.cb] = undefined;
20267                 try{
20268                     delete window[trans.cb];
20269                 }catch(e){}
20270             };
20271         }
20272     },
20273
20274     // private
20275     handleResponse : function(o, trans){
20276         this.trans = false;
20277         this.destroyTrans(trans, true);
20278         var result;
20279         try {
20280             result = trans.reader.readRecords(o);
20281         }catch(e){
20282             this.fireEvent("loadexception", this, o, trans.arg, e);
20283             trans.callback.call(trans.scope||window, null, trans.arg, false);
20284             return;
20285         }
20286         this.fireEvent("load", this, o, trans.arg);
20287         trans.callback.call(trans.scope||window, result, trans.arg, true);
20288     },
20289
20290     // private
20291     handleFailure : function(trans){
20292         this.trans = false;
20293         this.destroyTrans(trans, false);
20294         this.fireEvent("loadexception", this, null, trans.arg);
20295         trans.callback.call(trans.scope||window, null, trans.arg, false);
20296     }
20297 });/*
20298  * Based on:
20299  * Ext JS Library 1.1.1
20300  * Copyright(c) 2006-2007, Ext JS, LLC.
20301  *
20302  * Originally Released Under LGPL - original licence link has changed is not relivant.
20303  *
20304  * Fork - LGPL
20305  * <script type="text/javascript">
20306  */
20307
20308 /**
20309  * @class Roo.data.JsonReader
20310  * @extends Roo.data.DataReader
20311  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20312  * based on mappings in a provided Roo.data.Record constructor.
20313  * <p>
20314  * Example code:
20315  * <pre><code>
20316 var RecordDef = Roo.data.Record.create([
20317     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20318     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20319 ]);
20320 var myReader = new Roo.data.JsonReader({
20321     totalProperty: "results",    // The property which contains the total dataset size (optional)
20322     root: "rows",                // The property which contains an Array of row objects
20323     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20324 }, RecordDef);
20325 </code></pre>
20326  * <p>
20327  * This would consume a JSON file like this:
20328  * <pre><code>
20329 { 'results': 2, 'rows': [
20330     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20331     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20332 }
20333 </code></pre>
20334  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20335  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20336  * paged from the remote server.
20337  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20338  * @cfg {String} root name of the property which contains the Array of row objects.
20339  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20340  * @constructor
20341  * Create a new JsonReader
20342  * @param {Object} meta Metadata configuration options
20343  * @param {Object} recordType Either an Array of field definition objects,
20344  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20345  */
20346 Roo.data.JsonReader = function(meta, recordType){
20347     
20348     meta = meta || {};
20349     // set some defaults:
20350     Roo.applyIf(meta, {
20351         totalProperty: 'total',
20352         successProperty : 'success',
20353         root : 'data',
20354         id : 'id'
20355     });
20356     
20357     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20358 };
20359 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20360     /**
20361      * This method is only used by a DataProxy which has retrieved data from a remote server.
20362      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20363      * @return {Object} data A data block which is used by an Roo.data.Store object as
20364      * a cache of Roo.data.Records.
20365      */
20366     read : function(response){
20367         var json = response.responseText;
20368        
20369         var o = /* eval:var:o */ eval("("+json+")");
20370         if(!o) {
20371             throw {message: "JsonReader.read: Json object not found"};
20372         }
20373         
20374         if(o.metaData){
20375             delete this.ef;
20376             this.meta = o.metaData;
20377             this.recordType = Roo.data.Record.create(o.metaData.fields);
20378             this.onMetaChange(this.meta, this.recordType, o);
20379         }
20380         return this.readRecords(o);
20381     },
20382
20383     // private function a store will implement
20384     onMetaChange : function(meta, recordType, o){
20385
20386     },
20387
20388     /**
20389          * @ignore
20390          */
20391     simpleAccess: function(obj, subsc) {
20392         return obj[subsc];
20393     },
20394
20395         /**
20396          * @ignore
20397          */
20398     getJsonAccessor: function(){
20399         var re = /[\[\.]/;
20400         return function(expr) {
20401             try {
20402                 return(re.test(expr))
20403                     ? new Function("obj", "return obj." + expr)
20404                     : function(obj){
20405                         return obj[expr];
20406                     };
20407             } catch(e){}
20408             return Roo.emptyFn;
20409         };
20410     }(),
20411
20412     /**
20413      * Create a data block containing Roo.data.Records from an XML document.
20414      * @param {Object} o An object which contains an Array of row objects in the property specified
20415      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20416      * which contains the total size of the dataset.
20417      * @return {Object} data A data block which is used by an Roo.data.Store object as
20418      * a cache of Roo.data.Records.
20419      */
20420     readRecords : function(o){
20421         /**
20422          * After any data loads, the raw JSON data is available for further custom processing.
20423          * @type Object
20424          */
20425         this.jsonData = o;
20426         var s = this.meta, Record = this.recordType,
20427             f = Record.prototype.fields, fi = f.items, fl = f.length;
20428
20429 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20430         if (!this.ef) {
20431             if(s.totalProperty) {
20432                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20433                 }
20434                 if(s.successProperty) {
20435                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20436                 }
20437                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20438                 if (s.id) {
20439                         var g = this.getJsonAccessor(s.id);
20440                         this.getId = function(rec) {
20441                                 var r = g(rec);
20442                                 return (r === undefined || r === "") ? null : r;
20443                         };
20444                 } else {
20445                         this.getId = function(){return null;};
20446                 }
20447             this.ef = [];
20448             for(var jj = 0; jj < fl; jj++){
20449                 f = fi[jj];
20450                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20451                 this.ef[jj] = this.getJsonAccessor(map);
20452             }
20453         }
20454
20455         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20456         if(s.totalProperty){
20457             var vt = parseInt(this.getTotal(o), 10);
20458             if(!isNaN(vt)){
20459                 totalRecords = vt;
20460             }
20461         }
20462         if(s.successProperty){
20463             var vs = this.getSuccess(o);
20464             if(vs === false || vs === 'false'){
20465                 success = false;
20466             }
20467         }
20468         var records = [];
20469             for(var i = 0; i < c; i++){
20470                     var n = root[i];
20471                 var values = {};
20472                 var id = this.getId(n);
20473                 for(var j = 0; j < fl; j++){
20474                     f = fi[j];
20475                 var v = this.ef[j](n);
20476                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20477                 }
20478                 var record = new Record(values, id);
20479                 record.json = n;
20480                 records[i] = record;
20481             }
20482             return {
20483                 success : success,
20484                 records : records,
20485                 totalRecords : totalRecords
20486             };
20487     }
20488 });/*
20489  * Based on:
20490  * Ext JS Library 1.1.1
20491  * Copyright(c) 2006-2007, Ext JS, LLC.
20492  *
20493  * Originally Released Under LGPL - original licence link has changed is not relivant.
20494  *
20495  * Fork - LGPL
20496  * <script type="text/javascript">
20497  */
20498
20499 /**
20500  * @class Roo.data.XmlReader
20501  * @extends Roo.data.DataReader
20502  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20503  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20504  * <p>
20505  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20506  * header in the HTTP response must be set to "text/xml".</em>
20507  * <p>
20508  * Example code:
20509  * <pre><code>
20510 var RecordDef = Roo.data.Record.create([
20511    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20512    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20513 ]);
20514 var myReader = new Roo.data.XmlReader({
20515    totalRecords: "results", // The element which contains the total dataset size (optional)
20516    record: "row",           // The repeated element which contains row information
20517    id: "id"                 // The element within the row that provides an ID for the record (optional)
20518 }, RecordDef);
20519 </code></pre>
20520  * <p>
20521  * This would consume an XML file like this:
20522  * <pre><code>
20523 &lt;?xml?>
20524 &lt;dataset>
20525  &lt;results>2&lt;/results>
20526  &lt;row>
20527    &lt;id>1&lt;/id>
20528    &lt;name>Bill&lt;/name>
20529    &lt;occupation>Gardener&lt;/occupation>
20530  &lt;/row>
20531  &lt;row>
20532    &lt;id>2&lt;/id>
20533    &lt;name>Ben&lt;/name>
20534    &lt;occupation>Horticulturalist&lt;/occupation>
20535  &lt;/row>
20536 &lt;/dataset>
20537 </code></pre>
20538  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20539  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20540  * paged from the remote server.
20541  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20542  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20543  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20544  * a record identifier value.
20545  * @constructor
20546  * Create a new XmlReader
20547  * @param {Object} meta Metadata configuration options
20548  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20549  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20550  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20551  */
20552 Roo.data.XmlReader = function(meta, recordType){
20553     meta = meta || {};
20554     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20555 };
20556 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20557     /**
20558      * This method is only used by a DataProxy which has retrieved data from a remote server.
20559          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20560          * to contain a method called 'responseXML' that returns an XML document object.
20561      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20562      * a cache of Roo.data.Records.
20563      */
20564     read : function(response){
20565         var doc = response.responseXML;
20566         if(!doc) {
20567             throw {message: "XmlReader.read: XML Document not available"};
20568         }
20569         return this.readRecords(doc);
20570     },
20571
20572     /**
20573      * Create a data block containing Roo.data.Records from an XML document.
20574          * @param {Object} doc A parsed XML document.
20575      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20576      * a cache of Roo.data.Records.
20577      */
20578     readRecords : function(doc){
20579         /**
20580          * After any data loads/reads, the raw XML Document is available for further custom processing.
20581          * @type XMLDocument
20582          */
20583         this.xmlData = doc;
20584         var root = doc.documentElement || doc;
20585         var q = Roo.DomQuery;
20586         var recordType = this.recordType, fields = recordType.prototype.fields;
20587         var sid = this.meta.id;
20588         var totalRecords = 0, success = true;
20589         if(this.meta.totalRecords){
20590             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20591         }
20592         
20593         if(this.meta.success){
20594             var sv = q.selectValue(this.meta.success, root, true);
20595             success = sv !== false && sv !== 'false';
20596         }
20597         var records = [];
20598         var ns = q.select(this.meta.record, root);
20599         for(var i = 0, len = ns.length; i < len; i++) {
20600                 var n = ns[i];
20601                 var values = {};
20602                 var id = sid ? q.selectValue(sid, n) : undefined;
20603                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20604                     var f = fields.items[j];
20605                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20606                     v = f.convert(v);
20607                     values[f.name] = v;
20608                 }
20609                 var record = new recordType(values, id);
20610                 record.node = n;
20611                 records[records.length] = record;
20612             }
20613
20614             return {
20615                 success : success,
20616                 records : records,
20617                 totalRecords : totalRecords || records.length
20618             };
20619     }
20620 });/*
20621  * Based on:
20622  * Ext JS Library 1.1.1
20623  * Copyright(c) 2006-2007, Ext JS, LLC.
20624  *
20625  * Originally Released Under LGPL - original licence link has changed is not relivant.
20626  *
20627  * Fork - LGPL
20628  * <script type="text/javascript">
20629  */
20630
20631 /**
20632  * @class Roo.data.ArrayReader
20633  * @extends Roo.data.DataReader
20634  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20635  * Each element of that Array represents a row of data fields. The
20636  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20637  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20638  * <p>
20639  * Example code:.
20640  * <pre><code>
20641 var RecordDef = Roo.data.Record.create([
20642     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20643     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20644 ]);
20645 var myReader = new Roo.data.ArrayReader({
20646     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20647 }, RecordDef);
20648 </code></pre>
20649  * <p>
20650  * This would consume an Array like this:
20651  * <pre><code>
20652 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20653   </code></pre>
20654  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20655  * @constructor
20656  * Create a new JsonReader
20657  * @param {Object} meta Metadata configuration options.
20658  * @param {Object} recordType Either an Array of field definition objects
20659  * as specified to {@link Roo.data.Record#create},
20660  * or an {@link Roo.data.Record} object
20661  * created using {@link Roo.data.Record#create}.
20662  */
20663 Roo.data.ArrayReader = function(meta, recordType){
20664     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20665 };
20666
20667 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20668     /**
20669      * Create a data block containing Roo.data.Records from an XML document.
20670      * @param {Object} o An Array of row objects which represents the dataset.
20671      * @return {Object} data A data block which is used by an Roo.data.Store object as
20672      * a cache of Roo.data.Records.
20673      */
20674     readRecords : function(o){
20675         var sid = this.meta ? this.meta.id : null;
20676         var recordType = this.recordType, fields = recordType.prototype.fields;
20677         var records = [];
20678         var root = o;
20679             for(var i = 0; i < root.length; i++){
20680                     var n = root[i];
20681                 var values = {};
20682                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20683                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20684                 var f = fields.items[j];
20685                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20686                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20687                 v = f.convert(v);
20688                 values[f.name] = v;
20689             }
20690                 var record = new recordType(values, id);
20691                 record.json = n;
20692                 records[records.length] = record;
20693             }
20694             return {
20695                 records : records,
20696                 totalRecords : records.length
20697             };
20698     }
20699 });/*
20700  * Based on:
20701  * Ext JS Library 1.1.1
20702  * Copyright(c) 2006-2007, Ext JS, LLC.
20703  *
20704  * Originally Released Under LGPL - original licence link has changed is not relivant.
20705  *
20706  * Fork - LGPL
20707  * <script type="text/javascript">
20708  */
20709
20710
20711 /**
20712  * @class Roo.data.Tree
20713  * @extends Roo.util.Observable
20714  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20715  * in the tree have most standard DOM functionality.
20716  * @constructor
20717  * @param {Node} root (optional) The root node
20718  */
20719 Roo.data.Tree = function(root){
20720    this.nodeHash = {};
20721    /**
20722     * The root node for this tree
20723     * @type Node
20724     */
20725    this.root = null;
20726    if(root){
20727        this.setRootNode(root);
20728    }
20729    this.addEvents({
20730        /**
20731         * @event append
20732         * Fires when a new child node is appended to a node in this tree.
20733         * @param {Tree} tree The owner tree
20734         * @param {Node} parent The parent node
20735         * @param {Node} node The newly appended node
20736         * @param {Number} index The index of the newly appended node
20737         */
20738        "append" : true,
20739        /**
20740         * @event remove
20741         * Fires when a child node is removed from a node in this tree.
20742         * @param {Tree} tree The owner tree
20743         * @param {Node} parent The parent node
20744         * @param {Node} node The child node removed
20745         */
20746        "remove" : true,
20747        /**
20748         * @event move
20749         * Fires when a node is moved to a new location in the tree
20750         * @param {Tree} tree The owner tree
20751         * @param {Node} node The node moved
20752         * @param {Node} oldParent The old parent of this node
20753         * @param {Node} newParent The new parent of this node
20754         * @param {Number} index The index it was moved to
20755         */
20756        "move" : true,
20757        /**
20758         * @event insert
20759         * Fires when a new child node is inserted in a node in this tree.
20760         * @param {Tree} tree The owner tree
20761         * @param {Node} parent The parent node
20762         * @param {Node} node The child node inserted
20763         * @param {Node} refNode The child node the node was inserted before
20764         */
20765        "insert" : true,
20766        /**
20767         * @event beforeappend
20768         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20769         * @param {Tree} tree The owner tree
20770         * @param {Node} parent The parent node
20771         * @param {Node} node The child node to be appended
20772         */
20773        "beforeappend" : true,
20774        /**
20775         * @event beforeremove
20776         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20777         * @param {Tree} tree The owner tree
20778         * @param {Node} parent The parent node
20779         * @param {Node} node The child node to be removed
20780         */
20781        "beforeremove" : true,
20782        /**
20783         * @event beforemove
20784         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20785         * @param {Tree} tree The owner tree
20786         * @param {Node} node The node being moved
20787         * @param {Node} oldParent The parent of the node
20788         * @param {Node} newParent The new parent the node is moving to
20789         * @param {Number} index The index it is being moved to
20790         */
20791        "beforemove" : true,
20792        /**
20793         * @event beforeinsert
20794         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20795         * @param {Tree} tree The owner tree
20796         * @param {Node} parent The parent node
20797         * @param {Node} node The child node to be inserted
20798         * @param {Node} refNode The child node the node is being inserted before
20799         */
20800        "beforeinsert" : true
20801    });
20802
20803     Roo.data.Tree.superclass.constructor.call(this);
20804 };
20805
20806 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20807     pathSeparator: "/",
20808
20809     proxyNodeEvent : function(){
20810         return this.fireEvent.apply(this, arguments);
20811     },
20812
20813     /**
20814      * Returns the root node for this tree.
20815      * @return {Node}
20816      */
20817     getRootNode : function(){
20818         return this.root;
20819     },
20820
20821     /**
20822      * Sets the root node for this tree.
20823      * @param {Node} node
20824      * @return {Node}
20825      */
20826     setRootNode : function(node){
20827         this.root = node;
20828         node.ownerTree = this;
20829         node.isRoot = true;
20830         this.registerNode(node);
20831         return node;
20832     },
20833
20834     /**
20835      * Gets a node in this tree by its id.
20836      * @param {String} id
20837      * @return {Node}
20838      */
20839     getNodeById : function(id){
20840         return this.nodeHash[id];
20841     },
20842
20843     registerNode : function(node){
20844         this.nodeHash[node.id] = node;
20845     },
20846
20847     unregisterNode : function(node){
20848         delete this.nodeHash[node.id];
20849     },
20850
20851     toString : function(){
20852         return "[Tree"+(this.id?" "+this.id:"")+"]";
20853     }
20854 });
20855
20856 /**
20857  * @class Roo.data.Node
20858  * @extends Roo.util.Observable
20859  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20860  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20861  * @constructor
20862  * @param {Object} attributes The attributes/config for the node
20863  */
20864 Roo.data.Node = function(attributes){
20865     /**
20866      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20867      * @type {Object}
20868      */
20869     this.attributes = attributes || {};
20870     this.leaf = this.attributes.leaf;
20871     /**
20872      * The node id. @type String
20873      */
20874     this.id = this.attributes.id;
20875     if(!this.id){
20876         this.id = Roo.id(null, "ynode-");
20877         this.attributes.id = this.id;
20878     }
20879     /**
20880      * All child nodes of this node. @type Array
20881      */
20882     this.childNodes = [];
20883     if(!this.childNodes.indexOf){ // indexOf is a must
20884         this.childNodes.indexOf = function(o){
20885             for(var i = 0, len = this.length; i < len; i++){
20886                 if(this[i] == o) {
20887                     return i;
20888                 }
20889             }
20890             return -1;
20891         };
20892     }
20893     /**
20894      * The parent node for this node. @type Node
20895      */
20896     this.parentNode = null;
20897     /**
20898      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20899      */
20900     this.firstChild = null;
20901     /**
20902      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20903      */
20904     this.lastChild = null;
20905     /**
20906      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20907      */
20908     this.previousSibling = null;
20909     /**
20910      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20911      */
20912     this.nextSibling = null;
20913
20914     this.addEvents({
20915        /**
20916         * @event append
20917         * Fires when a new child node is appended
20918         * @param {Tree} tree The owner tree
20919         * @param {Node} this This node
20920         * @param {Node} node The newly appended node
20921         * @param {Number} index The index of the newly appended node
20922         */
20923        "append" : true,
20924        /**
20925         * @event remove
20926         * Fires when a child node is removed
20927         * @param {Tree} tree The owner tree
20928         * @param {Node} this This node
20929         * @param {Node} node The removed node
20930         */
20931        "remove" : true,
20932        /**
20933         * @event move
20934         * Fires when this node is moved to a new location in the tree
20935         * @param {Tree} tree The owner tree
20936         * @param {Node} this This node
20937         * @param {Node} oldParent The old parent of this node
20938         * @param {Node} newParent The new parent of this node
20939         * @param {Number} index The index it was moved to
20940         */
20941        "move" : true,
20942        /**
20943         * @event insert
20944         * Fires when a new child node is inserted.
20945         * @param {Tree} tree The owner tree
20946         * @param {Node} this This node
20947         * @param {Node} node The child node inserted
20948         * @param {Node} refNode The child node the node was inserted before
20949         */
20950        "insert" : true,
20951        /**
20952         * @event beforeappend
20953         * Fires before a new child is appended, return false to cancel the append.
20954         * @param {Tree} tree The owner tree
20955         * @param {Node} this This node
20956         * @param {Node} node The child node to be appended
20957         */
20958        "beforeappend" : true,
20959        /**
20960         * @event beforeremove
20961         * Fires before a child is removed, return false to cancel the remove.
20962         * @param {Tree} tree The owner tree
20963         * @param {Node} this This node
20964         * @param {Node} node The child node to be removed
20965         */
20966        "beforeremove" : true,
20967        /**
20968         * @event beforemove
20969         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20970         * @param {Tree} tree The owner tree
20971         * @param {Node} this This node
20972         * @param {Node} oldParent The parent of this node
20973         * @param {Node} newParent The new parent this node is moving to
20974         * @param {Number} index The index it is being moved to
20975         */
20976        "beforemove" : true,
20977        /**
20978         * @event beforeinsert
20979         * Fires before a new child is inserted, return false to cancel the insert.
20980         * @param {Tree} tree The owner tree
20981         * @param {Node} this This node
20982         * @param {Node} node The child node to be inserted
20983         * @param {Node} refNode The child node the node is being inserted before
20984         */
20985        "beforeinsert" : true
20986    });
20987     this.listeners = this.attributes.listeners;
20988     Roo.data.Node.superclass.constructor.call(this);
20989 };
20990
20991 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20992     fireEvent : function(evtName){
20993         // first do standard event for this node
20994         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20995             return false;
20996         }
20997         // then bubble it up to the tree if the event wasn't cancelled
20998         var ot = this.getOwnerTree();
20999         if(ot){
21000             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21001                 return false;
21002             }
21003         }
21004         return true;
21005     },
21006
21007     /**
21008      * Returns true if this node is a leaf
21009      * @return {Boolean}
21010      */
21011     isLeaf : function(){
21012         return this.leaf === true;
21013     },
21014
21015     // private
21016     setFirstChild : function(node){
21017         this.firstChild = node;
21018     },
21019
21020     //private
21021     setLastChild : function(node){
21022         this.lastChild = node;
21023     },
21024
21025
21026     /**
21027      * Returns true if this node is the last child of its parent
21028      * @return {Boolean}
21029      */
21030     isLast : function(){
21031        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21032     },
21033
21034     /**
21035      * Returns true if this node is the first child of its parent
21036      * @return {Boolean}
21037      */
21038     isFirst : function(){
21039        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21040     },
21041
21042     hasChildNodes : function(){
21043         return !this.isLeaf() && this.childNodes.length > 0;
21044     },
21045
21046     /**
21047      * Insert node(s) as the last child node of this node.
21048      * @param {Node/Array} node The node or Array of nodes to append
21049      * @return {Node} The appended node if single append, or null if an array was passed
21050      */
21051     appendChild : function(node){
21052         var multi = false;
21053         if(node instanceof Array){
21054             multi = node;
21055         }else if(arguments.length > 1){
21056             multi = arguments;
21057         }
21058         // if passed an array or multiple args do them one by one
21059         if(multi){
21060             for(var i = 0, len = multi.length; i < len; i++) {
21061                 this.appendChild(multi[i]);
21062             }
21063         }else{
21064             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21065                 return false;
21066             }
21067             var index = this.childNodes.length;
21068             var oldParent = node.parentNode;
21069             // it's a move, make sure we move it cleanly
21070             if(oldParent){
21071                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21072                     return false;
21073                 }
21074                 oldParent.removeChild(node);
21075             }
21076             index = this.childNodes.length;
21077             if(index == 0){
21078                 this.setFirstChild(node);
21079             }
21080             this.childNodes.push(node);
21081             node.parentNode = this;
21082             var ps = this.childNodes[index-1];
21083             if(ps){
21084                 node.previousSibling = ps;
21085                 ps.nextSibling = node;
21086             }else{
21087                 node.previousSibling = null;
21088             }
21089             node.nextSibling = null;
21090             this.setLastChild(node);
21091             node.setOwnerTree(this.getOwnerTree());
21092             this.fireEvent("append", this.ownerTree, this, node, index);
21093             if(oldParent){
21094                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21095             }
21096             return node;
21097         }
21098     },
21099
21100     /**
21101      * Removes a child node from this node.
21102      * @param {Node} node The node to remove
21103      * @return {Node} The removed node
21104      */
21105     removeChild : function(node){
21106         var index = this.childNodes.indexOf(node);
21107         if(index == -1){
21108             return false;
21109         }
21110         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21111             return false;
21112         }
21113
21114         // remove it from childNodes collection
21115         this.childNodes.splice(index, 1);
21116
21117         // update siblings
21118         if(node.previousSibling){
21119             node.previousSibling.nextSibling = node.nextSibling;
21120         }
21121         if(node.nextSibling){
21122             node.nextSibling.previousSibling = node.previousSibling;
21123         }
21124
21125         // update child refs
21126         if(this.firstChild == node){
21127             this.setFirstChild(node.nextSibling);
21128         }
21129         if(this.lastChild == node){
21130             this.setLastChild(node.previousSibling);
21131         }
21132
21133         node.setOwnerTree(null);
21134         // clear any references from the node
21135         node.parentNode = null;
21136         node.previousSibling = null;
21137         node.nextSibling = null;
21138         this.fireEvent("remove", this.ownerTree, this, node);
21139         return node;
21140     },
21141
21142     /**
21143      * Inserts the first node before the second node in this nodes childNodes collection.
21144      * @param {Node} node The node to insert
21145      * @param {Node} refNode The node to insert before (if null the node is appended)
21146      * @return {Node} The inserted node
21147      */
21148     insertBefore : function(node, refNode){
21149         if(!refNode){ // like standard Dom, refNode can be null for append
21150             return this.appendChild(node);
21151         }
21152         // nothing to do
21153         if(node == refNode){
21154             return false;
21155         }
21156
21157         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21158             return false;
21159         }
21160         var index = this.childNodes.indexOf(refNode);
21161         var oldParent = node.parentNode;
21162         var refIndex = index;
21163
21164         // when moving internally, indexes will change after remove
21165         if(oldParent == this && this.childNodes.indexOf(node) < index){
21166             refIndex--;
21167         }
21168
21169         // it's a move, make sure we move it cleanly
21170         if(oldParent){
21171             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21172                 return false;
21173             }
21174             oldParent.removeChild(node);
21175         }
21176         if(refIndex == 0){
21177             this.setFirstChild(node);
21178         }
21179         this.childNodes.splice(refIndex, 0, node);
21180         node.parentNode = this;
21181         var ps = this.childNodes[refIndex-1];
21182         if(ps){
21183             node.previousSibling = ps;
21184             ps.nextSibling = node;
21185         }else{
21186             node.previousSibling = null;
21187         }
21188         node.nextSibling = refNode;
21189         refNode.previousSibling = node;
21190         node.setOwnerTree(this.getOwnerTree());
21191         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21192         if(oldParent){
21193             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21194         }
21195         return node;
21196     },
21197
21198     /**
21199      * Returns the child node at the specified index.
21200      * @param {Number} index
21201      * @return {Node}
21202      */
21203     item : function(index){
21204         return this.childNodes[index];
21205     },
21206
21207     /**
21208      * Replaces one child node in this node with another.
21209      * @param {Node} newChild The replacement node
21210      * @param {Node} oldChild The node to replace
21211      * @return {Node} The replaced node
21212      */
21213     replaceChild : function(newChild, oldChild){
21214         this.insertBefore(newChild, oldChild);
21215         this.removeChild(oldChild);
21216         return oldChild;
21217     },
21218
21219     /**
21220      * Returns the index of a child node
21221      * @param {Node} node
21222      * @return {Number} The index of the node or -1 if it was not found
21223      */
21224     indexOf : function(child){
21225         return this.childNodes.indexOf(child);
21226     },
21227
21228     /**
21229      * Returns the tree this node is in.
21230      * @return {Tree}
21231      */
21232     getOwnerTree : function(){
21233         // if it doesn't have one, look for one
21234         if(!this.ownerTree){
21235             var p = this;
21236             while(p){
21237                 if(p.ownerTree){
21238                     this.ownerTree = p.ownerTree;
21239                     break;
21240                 }
21241                 p = p.parentNode;
21242             }
21243         }
21244         return this.ownerTree;
21245     },
21246
21247     /**
21248      * Returns depth of this node (the root node has a depth of 0)
21249      * @return {Number}
21250      */
21251     getDepth : function(){
21252         var depth = 0;
21253         var p = this;
21254         while(p.parentNode){
21255             ++depth;
21256             p = p.parentNode;
21257         }
21258         return depth;
21259     },
21260
21261     // private
21262     setOwnerTree : function(tree){
21263         // if it's move, we need to update everyone
21264         if(tree != this.ownerTree){
21265             if(this.ownerTree){
21266                 this.ownerTree.unregisterNode(this);
21267             }
21268             this.ownerTree = tree;
21269             var cs = this.childNodes;
21270             for(var i = 0, len = cs.length; i < len; i++) {
21271                 cs[i].setOwnerTree(tree);
21272             }
21273             if(tree){
21274                 tree.registerNode(this);
21275             }
21276         }
21277     },
21278
21279     /**
21280      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21281      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21282      * @return {String} The path
21283      */
21284     getPath : function(attr){
21285         attr = attr || "id";
21286         var p = this.parentNode;
21287         var b = [this.attributes[attr]];
21288         while(p){
21289             b.unshift(p.attributes[attr]);
21290             p = p.parentNode;
21291         }
21292         var sep = this.getOwnerTree().pathSeparator;
21293         return sep + b.join(sep);
21294     },
21295
21296     /**
21297      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21298      * function call will be the scope provided or the current node. The arguments to the function
21299      * will be the args provided or the current node. If the function returns false at any point,
21300      * the bubble is stopped.
21301      * @param {Function} fn The function to call
21302      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21303      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21304      */
21305     bubble : function(fn, scope, args){
21306         var p = this;
21307         while(p){
21308             if(fn.call(scope || p, args || p) === false){
21309                 break;
21310             }
21311             p = p.parentNode;
21312         }
21313     },
21314
21315     /**
21316      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21317      * function call will be the scope provided or the current node. The arguments to the function
21318      * will be the args provided or the current node. If the function returns false at any point,
21319      * the cascade is stopped on that branch.
21320      * @param {Function} fn The function to call
21321      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21322      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21323      */
21324     cascade : function(fn, scope, args){
21325         if(fn.call(scope || this, args || this) !== false){
21326             var cs = this.childNodes;
21327             for(var i = 0, len = cs.length; i < len; i++) {
21328                 cs[i].cascade(fn, scope, args);
21329             }
21330         }
21331     },
21332
21333     /**
21334      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21335      * function call will be the scope provided or the current node. The arguments to the function
21336      * will be the args provided or the current node. If the function returns false at any point,
21337      * the iteration stops.
21338      * @param {Function} fn The function to call
21339      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21340      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21341      */
21342     eachChild : function(fn, scope, args){
21343         var cs = this.childNodes;
21344         for(var i = 0, len = cs.length; i < len; i++) {
21345                 if(fn.call(scope || this, args || cs[i]) === false){
21346                     break;
21347                 }
21348         }
21349     },
21350
21351     /**
21352      * Finds the first child that has the attribute with the specified value.
21353      * @param {String} attribute The attribute name
21354      * @param {Mixed} value The value to search for
21355      * @return {Node} The found child or null if none was found
21356      */
21357     findChild : function(attribute, value){
21358         var cs = this.childNodes;
21359         for(var i = 0, len = cs.length; i < len; i++) {
21360                 if(cs[i].attributes[attribute] == value){
21361                     return cs[i];
21362                 }
21363         }
21364         return null;
21365     },
21366
21367     /**
21368      * Finds the first child by a custom function. The child matches if the function passed
21369      * returns true.
21370      * @param {Function} fn
21371      * @param {Object} scope (optional)
21372      * @return {Node} The found child or null if none was found
21373      */
21374     findChildBy : function(fn, scope){
21375         var cs = this.childNodes;
21376         for(var i = 0, len = cs.length; i < len; i++) {
21377                 if(fn.call(scope||cs[i], cs[i]) === true){
21378                     return cs[i];
21379                 }
21380         }
21381         return null;
21382     },
21383
21384     /**
21385      * Sorts this nodes children using the supplied sort function
21386      * @param {Function} fn
21387      * @param {Object} scope (optional)
21388      */
21389     sort : function(fn, scope){
21390         var cs = this.childNodes;
21391         var len = cs.length;
21392         if(len > 0){
21393             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21394             cs.sort(sortFn);
21395             for(var i = 0; i < len; i++){
21396                 var n = cs[i];
21397                 n.previousSibling = cs[i-1];
21398                 n.nextSibling = cs[i+1];
21399                 if(i == 0){
21400                     this.setFirstChild(n);
21401                 }
21402                 if(i == len-1){
21403                     this.setLastChild(n);
21404                 }
21405             }
21406         }
21407     },
21408
21409     /**
21410      * Returns true if this node is an ancestor (at any point) of the passed node.
21411      * @param {Node} node
21412      * @return {Boolean}
21413      */
21414     contains : function(node){
21415         return node.isAncestor(this);
21416     },
21417
21418     /**
21419      * Returns true if the passed node is an ancestor (at any point) of this node.
21420      * @param {Node} node
21421      * @return {Boolean}
21422      */
21423     isAncestor : function(node){
21424         var p = this.parentNode;
21425         while(p){
21426             if(p == node){
21427                 return true;
21428             }
21429             p = p.parentNode;
21430         }
21431         return false;
21432     },
21433
21434     toString : function(){
21435         return "[Node"+(this.id?" "+this.id:"")+"]";
21436     }
21437 });/*
21438  * Based on:
21439  * Ext JS Library 1.1.1
21440  * Copyright(c) 2006-2007, Ext JS, LLC.
21441  *
21442  * Originally Released Under LGPL - original licence link has changed is not relivant.
21443  *
21444  * Fork - LGPL
21445  * <script type="text/javascript">
21446  */
21447  
21448
21449 /**
21450  * @class Roo.ComponentMgr
21451  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21452  * @singleton
21453  */
21454 Roo.ComponentMgr = function(){
21455     var all = new Roo.util.MixedCollection();
21456
21457     return {
21458         /**
21459          * Registers a component.
21460          * @param {Roo.Component} c The component
21461          */
21462         register : function(c){
21463             all.add(c);
21464         },
21465
21466         /**
21467          * Unregisters a component.
21468          * @param {Roo.Component} c The component
21469          */
21470         unregister : function(c){
21471             all.remove(c);
21472         },
21473
21474         /**
21475          * Returns a component by id
21476          * @param {String} id The component id
21477          */
21478         get : function(id){
21479             return all.get(id);
21480         },
21481
21482         /**
21483          * Registers a function that will be called when a specified component is added to ComponentMgr
21484          * @param {String} id The component id
21485          * @param {Funtction} fn The callback function
21486          * @param {Object} scope The scope of the callback
21487          */
21488         onAvailable : function(id, fn, scope){
21489             all.on("add", function(index, o){
21490                 if(o.id == id){
21491                     fn.call(scope || o, o);
21492                     all.un("add", fn, scope);
21493                 }
21494             });
21495         }
21496     };
21497 }();/*
21498  * Based on:
21499  * Ext JS Library 1.1.1
21500  * Copyright(c) 2006-2007, Ext JS, LLC.
21501  *
21502  * Originally Released Under LGPL - original licence link has changed is not relivant.
21503  *
21504  * Fork - LGPL
21505  * <script type="text/javascript">
21506  */
21507  
21508 /**
21509  * @class Roo.Component
21510  * @extends Roo.util.Observable
21511  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21512  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21513  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21514  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21515  * All visual components (widgets) that require rendering into a layout should subclass Component.
21516  * @constructor
21517  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21518  * 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
21519  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21520  */
21521 Roo.Component = function(config){
21522     config = config || {};
21523     if(config.tagName || config.dom || typeof config == "string"){ // element object
21524         config = {el: config, id: config.id || config};
21525     }
21526     this.initialConfig = config;
21527
21528     Roo.apply(this, config);
21529     this.addEvents({
21530         /**
21531          * @event disable
21532          * Fires after the component is disabled.
21533              * @param {Roo.Component} this
21534              */
21535         disable : true,
21536         /**
21537          * @event enable
21538          * Fires after the component is enabled.
21539              * @param {Roo.Component} this
21540              */
21541         enable : true,
21542         /**
21543          * @event beforeshow
21544          * Fires before the component is shown.  Return false to stop the show.
21545              * @param {Roo.Component} this
21546              */
21547         beforeshow : true,
21548         /**
21549          * @event show
21550          * Fires after the component is shown.
21551              * @param {Roo.Component} this
21552              */
21553         show : true,
21554         /**
21555          * @event beforehide
21556          * Fires before the component is hidden. Return false to stop the hide.
21557              * @param {Roo.Component} this
21558              */
21559         beforehide : true,
21560         /**
21561          * @event hide
21562          * Fires after the component is hidden.
21563              * @param {Roo.Component} this
21564              */
21565         hide : true,
21566         /**
21567          * @event beforerender
21568          * Fires before the component is rendered. Return false to stop the render.
21569              * @param {Roo.Component} this
21570              */
21571         beforerender : true,
21572         /**
21573          * @event render
21574          * Fires after the component is rendered.
21575              * @param {Roo.Component} this
21576              */
21577         render : true,
21578         /**
21579          * @event beforedestroy
21580          * Fires before the component is destroyed. Return false to stop the destroy.
21581              * @param {Roo.Component} this
21582              */
21583         beforedestroy : true,
21584         /**
21585          * @event destroy
21586          * Fires after the component is destroyed.
21587              * @param {Roo.Component} this
21588              */
21589         destroy : true
21590     });
21591     if(!this.id){
21592         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21593     }
21594     Roo.ComponentMgr.register(this);
21595     Roo.Component.superclass.constructor.call(this);
21596     this.initComponent();
21597     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21598         this.render(this.renderTo);
21599         delete this.renderTo;
21600     }
21601 };
21602
21603 // private
21604 Roo.Component.AUTO_ID = 1000;
21605
21606 Roo.extend(Roo.Component, Roo.util.Observable, {
21607     /**
21608      * @property {Boolean} hidden
21609      * true if this component is hidden. Read-only.
21610      */
21611     hidden : false,
21612     /**
21613      * true if this component is disabled. Read-only.
21614      */
21615     disabled : false,
21616     /**
21617      * true if this component has been rendered. Read-only.
21618      */
21619     rendered : false,
21620     
21621     /** @cfg {String} disableClass
21622      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21623      */
21624     disabledClass : "x-item-disabled",
21625         /** @cfg {Boolean} allowDomMove
21626          * Whether the component can move the Dom node when rendering (defaults to true).
21627          */
21628     allowDomMove : true,
21629     /** @cfg {String} hideMode
21630      * How this component should hidden. Supported values are
21631      * "visibility" (css visibility), "offsets" (negative offset position) and
21632      * "display" (css display) - defaults to "display".
21633      */
21634     hideMode: 'display',
21635
21636     // private
21637     ctype : "Roo.Component",
21638
21639     /** @cfg {String} actionMode 
21640      * which property holds the element that used for  hide() / show() / disable() / enable()
21641      * default is 'el' 
21642      */
21643     actionMode : "el",
21644
21645     // private
21646     getActionEl : function(){
21647         return this[this.actionMode];
21648     },
21649
21650     initComponent : Roo.emptyFn,
21651     /**
21652      * If this is a lazy rendering component, render it to its container element.
21653      * @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.
21654      */
21655     render : function(container, position){
21656         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21657             if(!container && this.el){
21658                 this.el = Roo.get(this.el);
21659                 container = this.el.dom.parentNode;
21660                 this.allowDomMove = false;
21661             }
21662             this.container = Roo.get(container);
21663             this.rendered = true;
21664             if(position !== undefined){
21665                 if(typeof position == 'number'){
21666                     position = this.container.dom.childNodes[position];
21667                 }else{
21668                     position = Roo.getDom(position);
21669                 }
21670             }
21671             this.onRender(this.container, position || null);
21672             if(this.cls){
21673                 this.el.addClass(this.cls);
21674                 delete this.cls;
21675             }
21676             if(this.style){
21677                 this.el.applyStyles(this.style);
21678                 delete this.style;
21679             }
21680             this.fireEvent("render", this);
21681             this.afterRender(this.container);
21682             if(this.hidden){
21683                 this.hide();
21684             }
21685             if(this.disabled){
21686                 this.disable();
21687             }
21688         }
21689         return this;
21690     },
21691
21692     // private
21693     // default function is not really useful
21694     onRender : function(ct, position){
21695         if(this.el){
21696             this.el = Roo.get(this.el);
21697             if(this.allowDomMove !== false){
21698                 ct.dom.insertBefore(this.el.dom, position);
21699             }
21700         }
21701     },
21702
21703     // private
21704     getAutoCreate : function(){
21705         var cfg = typeof this.autoCreate == "object" ?
21706                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21707         if(this.id && !cfg.id){
21708             cfg.id = this.id;
21709         }
21710         return cfg;
21711     },
21712
21713     // private
21714     afterRender : Roo.emptyFn,
21715
21716     /**
21717      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21718      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21719      */
21720     destroy : function(){
21721         if(this.fireEvent("beforedestroy", this) !== false){
21722             this.purgeListeners();
21723             this.beforeDestroy();
21724             if(this.rendered){
21725                 this.el.removeAllListeners();
21726                 this.el.remove();
21727                 if(this.actionMode == "container"){
21728                     this.container.remove();
21729                 }
21730             }
21731             this.onDestroy();
21732             Roo.ComponentMgr.unregister(this);
21733             this.fireEvent("destroy", this);
21734         }
21735     },
21736
21737         // private
21738     beforeDestroy : function(){
21739
21740     },
21741
21742         // private
21743         onDestroy : function(){
21744
21745     },
21746
21747     /**
21748      * Returns the underlying {@link Roo.Element}.
21749      * @return {Roo.Element} The element
21750      */
21751     getEl : function(){
21752         return this.el;
21753     },
21754
21755     /**
21756      * Returns the id of this component.
21757      * @return {String}
21758      */
21759     getId : function(){
21760         return this.id;
21761     },
21762
21763     /**
21764      * Try to focus this component.
21765      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21766      * @return {Roo.Component} this
21767      */
21768     focus : function(selectText){
21769         if(this.rendered){
21770             this.el.focus();
21771             if(selectText === true){
21772                 this.el.dom.select();
21773             }
21774         }
21775         return this;
21776     },
21777
21778     // private
21779     blur : function(){
21780         if(this.rendered){
21781             this.el.blur();
21782         }
21783         return this;
21784     },
21785
21786     /**
21787      * Disable this component.
21788      * @return {Roo.Component} this
21789      */
21790     disable : function(){
21791         if(this.rendered){
21792             this.onDisable();
21793         }
21794         this.disabled = true;
21795         this.fireEvent("disable", this);
21796         return this;
21797     },
21798
21799         // private
21800     onDisable : function(){
21801         this.getActionEl().addClass(this.disabledClass);
21802         this.el.dom.disabled = true;
21803     },
21804
21805     /**
21806      * Enable this component.
21807      * @return {Roo.Component} this
21808      */
21809     enable : function(){
21810         if(this.rendered){
21811             this.onEnable();
21812         }
21813         this.disabled = false;
21814         this.fireEvent("enable", this);
21815         return this;
21816     },
21817
21818         // private
21819     onEnable : function(){
21820         this.getActionEl().removeClass(this.disabledClass);
21821         this.el.dom.disabled = false;
21822     },
21823
21824     /**
21825      * Convenience function for setting disabled/enabled by boolean.
21826      * @param {Boolean} disabled
21827      */
21828     setDisabled : function(disabled){
21829         this[disabled ? "disable" : "enable"]();
21830     },
21831
21832     /**
21833      * Show this component.
21834      * @return {Roo.Component} this
21835      */
21836     show: function(){
21837         if(this.fireEvent("beforeshow", this) !== false){
21838             this.hidden = false;
21839             if(this.rendered){
21840                 this.onShow();
21841             }
21842             this.fireEvent("show", this);
21843         }
21844         return this;
21845     },
21846
21847     // private
21848     onShow : function(){
21849         var ae = this.getActionEl();
21850         if(this.hideMode == 'visibility'){
21851             ae.dom.style.visibility = "visible";
21852         }else if(this.hideMode == 'offsets'){
21853             ae.removeClass('x-hidden');
21854         }else{
21855             ae.dom.style.display = "";
21856         }
21857     },
21858
21859     /**
21860      * Hide this component.
21861      * @return {Roo.Component} this
21862      */
21863     hide: function(){
21864         if(this.fireEvent("beforehide", this) !== false){
21865             this.hidden = true;
21866             if(this.rendered){
21867                 this.onHide();
21868             }
21869             this.fireEvent("hide", this);
21870         }
21871         return this;
21872     },
21873
21874     // private
21875     onHide : function(){
21876         var ae = this.getActionEl();
21877         if(this.hideMode == 'visibility'){
21878             ae.dom.style.visibility = "hidden";
21879         }else if(this.hideMode == 'offsets'){
21880             ae.addClass('x-hidden');
21881         }else{
21882             ae.dom.style.display = "none";
21883         }
21884     },
21885
21886     /**
21887      * Convenience function to hide or show this component by boolean.
21888      * @param {Boolean} visible True to show, false to hide
21889      * @return {Roo.Component} this
21890      */
21891     setVisible: function(visible){
21892         if(visible) {
21893             this.show();
21894         }else{
21895             this.hide();
21896         }
21897         return this;
21898     },
21899
21900     /**
21901      * Returns true if this component is visible.
21902      */
21903     isVisible : function(){
21904         return this.getActionEl().isVisible();
21905     },
21906
21907     cloneConfig : function(overrides){
21908         overrides = overrides || {};
21909         var id = overrides.id || Roo.id();
21910         var cfg = Roo.applyIf(overrides, this.initialConfig);
21911         cfg.id = id; // prevent dup id
21912         return new this.constructor(cfg);
21913     }
21914 });/*
21915  * Based on:
21916  * Ext JS Library 1.1.1
21917  * Copyright(c) 2006-2007, Ext JS, LLC.
21918  *
21919  * Originally Released Under LGPL - original licence link has changed is not relivant.
21920  *
21921  * Fork - LGPL
21922  * <script type="text/javascript">
21923  */
21924  (function(){ 
21925 /**
21926  * @class Roo.Layer
21927  * @extends Roo.Element
21928  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21929  * automatic maintaining of shadow/shim positions.
21930  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21931  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21932  * you can pass a string with a CSS class name. False turns off the shadow.
21933  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21934  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21935  * @cfg {String} cls CSS class to add to the element
21936  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21937  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21938  * @constructor
21939  * @param {Object} config An object with config options.
21940  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21941  */
21942
21943 Roo.Layer = function(config, existingEl){
21944     config = config || {};
21945     var dh = Roo.DomHelper;
21946     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21947     if(existingEl){
21948         this.dom = Roo.getDom(existingEl);
21949     }
21950     if(!this.dom){
21951         var o = config.dh || {tag: "div", cls: "x-layer"};
21952         this.dom = dh.append(pel, o);
21953     }
21954     if(config.cls){
21955         this.addClass(config.cls);
21956     }
21957     this.constrain = config.constrain !== false;
21958     this.visibilityMode = Roo.Element.VISIBILITY;
21959     if(config.id){
21960         this.id = this.dom.id = config.id;
21961     }else{
21962         this.id = Roo.id(this.dom);
21963     }
21964     this.zindex = config.zindex || this.getZIndex();
21965     this.position("absolute", this.zindex);
21966     if(config.shadow){
21967         this.shadowOffset = config.shadowOffset || 4;
21968         this.shadow = new Roo.Shadow({
21969             offset : this.shadowOffset,
21970             mode : config.shadow
21971         });
21972     }else{
21973         this.shadowOffset = 0;
21974     }
21975     this.useShim = config.shim !== false && Roo.useShims;
21976     this.useDisplay = config.useDisplay;
21977     this.hide();
21978 };
21979
21980 var supr = Roo.Element.prototype;
21981
21982 // shims are shared among layer to keep from having 100 iframes
21983 var shims = [];
21984
21985 Roo.extend(Roo.Layer, Roo.Element, {
21986
21987     getZIndex : function(){
21988         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21989     },
21990
21991     getShim : function(){
21992         if(!this.useShim){
21993             return null;
21994         }
21995         if(this.shim){
21996             return this.shim;
21997         }
21998         var shim = shims.shift();
21999         if(!shim){
22000             shim = this.createShim();
22001             shim.enableDisplayMode('block');
22002             shim.dom.style.display = 'none';
22003             shim.dom.style.visibility = 'visible';
22004         }
22005         var pn = this.dom.parentNode;
22006         if(shim.dom.parentNode != pn){
22007             pn.insertBefore(shim.dom, this.dom);
22008         }
22009         shim.setStyle('z-index', this.getZIndex()-2);
22010         this.shim = shim;
22011         return shim;
22012     },
22013
22014     hideShim : function(){
22015         if(this.shim){
22016             this.shim.setDisplayed(false);
22017             shims.push(this.shim);
22018             delete this.shim;
22019         }
22020     },
22021
22022     disableShadow : function(){
22023         if(this.shadow){
22024             this.shadowDisabled = true;
22025             this.shadow.hide();
22026             this.lastShadowOffset = this.shadowOffset;
22027             this.shadowOffset = 0;
22028         }
22029     },
22030
22031     enableShadow : function(show){
22032         if(this.shadow){
22033             this.shadowDisabled = false;
22034             this.shadowOffset = this.lastShadowOffset;
22035             delete this.lastShadowOffset;
22036             if(show){
22037                 this.sync(true);
22038             }
22039         }
22040     },
22041
22042     // private
22043     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22044     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22045     sync : function(doShow){
22046         var sw = this.shadow;
22047         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22048             var sh = this.getShim();
22049
22050             var w = this.getWidth(),
22051                 h = this.getHeight();
22052
22053             var l = this.getLeft(true),
22054                 t = this.getTop(true);
22055
22056             if(sw && !this.shadowDisabled){
22057                 if(doShow && !sw.isVisible()){
22058                     sw.show(this);
22059                 }else{
22060                     sw.realign(l, t, w, h);
22061                 }
22062                 if(sh){
22063                     if(doShow){
22064                        sh.show();
22065                     }
22066                     // fit the shim behind the shadow, so it is shimmed too
22067                     var a = sw.adjusts, s = sh.dom.style;
22068                     s.left = (Math.min(l, l+a.l))+"px";
22069                     s.top = (Math.min(t, t+a.t))+"px";
22070                     s.width = (w+a.w)+"px";
22071                     s.height = (h+a.h)+"px";
22072                 }
22073             }else if(sh){
22074                 if(doShow){
22075                    sh.show();
22076                 }
22077                 sh.setSize(w, h);
22078                 sh.setLeftTop(l, t);
22079             }
22080             
22081         }
22082     },
22083
22084     // private
22085     destroy : function(){
22086         this.hideShim();
22087         if(this.shadow){
22088             this.shadow.hide();
22089         }
22090         this.removeAllListeners();
22091         var pn = this.dom.parentNode;
22092         if(pn){
22093             pn.removeChild(this.dom);
22094         }
22095         Roo.Element.uncache(this.id);
22096     },
22097
22098     remove : function(){
22099         this.destroy();
22100     },
22101
22102     // private
22103     beginUpdate : function(){
22104         this.updating = true;
22105     },
22106
22107     // private
22108     endUpdate : function(){
22109         this.updating = false;
22110         this.sync(true);
22111     },
22112
22113     // private
22114     hideUnders : function(negOffset){
22115         if(this.shadow){
22116             this.shadow.hide();
22117         }
22118         this.hideShim();
22119     },
22120
22121     // private
22122     constrainXY : function(){
22123         if(this.constrain){
22124             var vw = Roo.lib.Dom.getViewWidth(),
22125                 vh = Roo.lib.Dom.getViewHeight();
22126             var s = Roo.get(document).getScroll();
22127
22128             var xy = this.getXY();
22129             var x = xy[0], y = xy[1];   
22130             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22131             // only move it if it needs it
22132             var moved = false;
22133             // first validate right/bottom
22134             if((x + w) > vw+s.left){
22135                 x = vw - w - this.shadowOffset;
22136                 moved = true;
22137             }
22138             if((y + h) > vh+s.top){
22139                 y = vh - h - this.shadowOffset;
22140                 moved = true;
22141             }
22142             // then make sure top/left isn't negative
22143             if(x < s.left){
22144                 x = s.left;
22145                 moved = true;
22146             }
22147             if(y < s.top){
22148                 y = s.top;
22149                 moved = true;
22150             }
22151             if(moved){
22152                 if(this.avoidY){
22153                     var ay = this.avoidY;
22154                     if(y <= ay && (y+h) >= ay){
22155                         y = ay-h-5;   
22156                     }
22157                 }
22158                 xy = [x, y];
22159                 this.storeXY(xy);
22160                 supr.setXY.call(this, xy);
22161                 this.sync();
22162             }
22163         }
22164     },
22165
22166     isVisible : function(){
22167         return this.visible;    
22168     },
22169
22170     // private
22171     showAction : function(){
22172         this.visible = true; // track visibility to prevent getStyle calls
22173         if(this.useDisplay === true){
22174             this.setDisplayed("");
22175         }else if(this.lastXY){
22176             supr.setXY.call(this, this.lastXY);
22177         }else if(this.lastLT){
22178             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22179         }
22180     },
22181
22182     // private
22183     hideAction : function(){
22184         this.visible = false;
22185         if(this.useDisplay === true){
22186             this.setDisplayed(false);
22187         }else{
22188             this.setLeftTop(-10000,-10000);
22189         }
22190     },
22191
22192     // overridden Element method
22193     setVisible : function(v, a, d, c, e){
22194         if(v){
22195             this.showAction();
22196         }
22197         if(a && v){
22198             var cb = function(){
22199                 this.sync(true);
22200                 if(c){
22201                     c();
22202                 }
22203             }.createDelegate(this);
22204             supr.setVisible.call(this, true, true, d, cb, e);
22205         }else{
22206             if(!v){
22207                 this.hideUnders(true);
22208             }
22209             var cb = c;
22210             if(a){
22211                 cb = function(){
22212                     this.hideAction();
22213                     if(c){
22214                         c();
22215                     }
22216                 }.createDelegate(this);
22217             }
22218             supr.setVisible.call(this, v, a, d, cb, e);
22219             if(v){
22220                 this.sync(true);
22221             }else if(!a){
22222                 this.hideAction();
22223             }
22224         }
22225     },
22226
22227     storeXY : function(xy){
22228         delete this.lastLT;
22229         this.lastXY = xy;
22230     },
22231
22232     storeLeftTop : function(left, top){
22233         delete this.lastXY;
22234         this.lastLT = [left, top];
22235     },
22236
22237     // private
22238     beforeFx : function(){
22239         this.beforeAction();
22240         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22241     },
22242
22243     // private
22244     afterFx : function(){
22245         Roo.Layer.superclass.afterFx.apply(this, arguments);
22246         this.sync(this.isVisible());
22247     },
22248
22249     // private
22250     beforeAction : function(){
22251         if(!this.updating && this.shadow){
22252             this.shadow.hide();
22253         }
22254     },
22255
22256     // overridden Element method
22257     setLeft : function(left){
22258         this.storeLeftTop(left, this.getTop(true));
22259         supr.setLeft.apply(this, arguments);
22260         this.sync();
22261     },
22262
22263     setTop : function(top){
22264         this.storeLeftTop(this.getLeft(true), top);
22265         supr.setTop.apply(this, arguments);
22266         this.sync();
22267     },
22268
22269     setLeftTop : function(left, top){
22270         this.storeLeftTop(left, top);
22271         supr.setLeftTop.apply(this, arguments);
22272         this.sync();
22273     },
22274
22275     setXY : function(xy, a, d, c, e){
22276         this.fixDisplay();
22277         this.beforeAction();
22278         this.storeXY(xy);
22279         var cb = this.createCB(c);
22280         supr.setXY.call(this, xy, a, d, cb, e);
22281         if(!a){
22282             cb();
22283         }
22284     },
22285
22286     // private
22287     createCB : function(c){
22288         var el = this;
22289         return function(){
22290             el.constrainXY();
22291             el.sync(true);
22292             if(c){
22293                 c();
22294             }
22295         };
22296     },
22297
22298     // overridden Element method
22299     setX : function(x, a, d, c, e){
22300         this.setXY([x, this.getY()], a, d, c, e);
22301     },
22302
22303     // overridden Element method
22304     setY : function(y, a, d, c, e){
22305         this.setXY([this.getX(), y], a, d, c, e);
22306     },
22307
22308     // overridden Element method
22309     setSize : function(w, h, a, d, c, e){
22310         this.beforeAction();
22311         var cb = this.createCB(c);
22312         supr.setSize.call(this, w, h, a, d, cb, e);
22313         if(!a){
22314             cb();
22315         }
22316     },
22317
22318     // overridden Element method
22319     setWidth : function(w, a, d, c, e){
22320         this.beforeAction();
22321         var cb = this.createCB(c);
22322         supr.setWidth.call(this, w, a, d, cb, e);
22323         if(!a){
22324             cb();
22325         }
22326     },
22327
22328     // overridden Element method
22329     setHeight : function(h, a, d, c, e){
22330         this.beforeAction();
22331         var cb = this.createCB(c);
22332         supr.setHeight.call(this, h, a, d, cb, e);
22333         if(!a){
22334             cb();
22335         }
22336     },
22337
22338     // overridden Element method
22339     setBounds : function(x, y, w, h, a, d, c, e){
22340         this.beforeAction();
22341         var cb = this.createCB(c);
22342         if(!a){
22343             this.storeXY([x, y]);
22344             supr.setXY.call(this, [x, y]);
22345             supr.setSize.call(this, w, h, a, d, cb, e);
22346             cb();
22347         }else{
22348             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22349         }
22350         return this;
22351     },
22352     
22353     /**
22354      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22355      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22356      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22357      * @param {Number} zindex The new z-index to set
22358      * @return {this} The Layer
22359      */
22360     setZIndex : function(zindex){
22361         this.zindex = zindex;
22362         this.setStyle("z-index", zindex + 2);
22363         if(this.shadow){
22364             this.shadow.setZIndex(zindex + 1);
22365         }
22366         if(this.shim){
22367             this.shim.setStyle("z-index", zindex);
22368         }
22369     }
22370 });
22371 })();/*
22372  * Based on:
22373  * Ext JS Library 1.1.1
22374  * Copyright(c) 2006-2007, Ext JS, LLC.
22375  *
22376  * Originally Released Under LGPL - original licence link has changed is not relivant.
22377  *
22378  * Fork - LGPL
22379  * <script type="text/javascript">
22380  */
22381
22382
22383 /**
22384  * @class Roo.Shadow
22385  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22386  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22387  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22388  * @constructor
22389  * Create a new Shadow
22390  * @param {Object} config The config object
22391  */
22392 Roo.Shadow = function(config){
22393     Roo.apply(this, config);
22394     if(typeof this.mode != "string"){
22395         this.mode = this.defaultMode;
22396     }
22397     var o = this.offset, a = {h: 0};
22398     var rad = Math.floor(this.offset/2);
22399     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22400         case "drop":
22401             a.w = 0;
22402             a.l = a.t = o;
22403             a.t -= 1;
22404             if(Roo.isIE){
22405                 a.l -= this.offset + rad;
22406                 a.t -= this.offset + rad;
22407                 a.w -= rad;
22408                 a.h -= rad;
22409                 a.t += 1;
22410             }
22411         break;
22412         case "sides":
22413             a.w = (o*2);
22414             a.l = -o;
22415             a.t = o-1;
22416             if(Roo.isIE){
22417                 a.l -= (this.offset - rad);
22418                 a.t -= this.offset + rad;
22419                 a.l += 1;
22420                 a.w -= (this.offset - rad)*2;
22421                 a.w -= rad + 1;
22422                 a.h -= 1;
22423             }
22424         break;
22425         case "frame":
22426             a.w = a.h = (o*2);
22427             a.l = a.t = -o;
22428             a.t += 1;
22429             a.h -= 2;
22430             if(Roo.isIE){
22431                 a.l -= (this.offset - rad);
22432                 a.t -= (this.offset - rad);
22433                 a.l += 1;
22434                 a.w -= (this.offset + rad + 1);
22435                 a.h -= (this.offset + rad);
22436                 a.h += 1;
22437             }
22438         break;
22439     };
22440
22441     this.adjusts = a;
22442 };
22443
22444 Roo.Shadow.prototype = {
22445     /**
22446      * @cfg {String} mode
22447      * The shadow display mode.  Supports the following options:<br />
22448      * sides: Shadow displays on both sides and bottom only<br />
22449      * frame: Shadow displays equally on all four sides<br />
22450      * drop: Traditional bottom-right drop shadow (default)
22451      */
22452     /**
22453      * @cfg {String} offset
22454      * The number of pixels to offset the shadow from the element (defaults to 4)
22455      */
22456     offset: 4,
22457
22458     // private
22459     defaultMode: "drop",
22460
22461     /**
22462      * Displays the shadow under the target element
22463      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22464      */
22465     show : function(target){
22466         target = Roo.get(target);
22467         if(!this.el){
22468             this.el = Roo.Shadow.Pool.pull();
22469             if(this.el.dom.nextSibling != target.dom){
22470                 this.el.insertBefore(target);
22471             }
22472         }
22473         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22474         if(Roo.isIE){
22475             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22476         }
22477         this.realign(
22478             target.getLeft(true),
22479             target.getTop(true),
22480             target.getWidth(),
22481             target.getHeight()
22482         );
22483         this.el.dom.style.display = "block";
22484     },
22485
22486     /**
22487      * Returns true if the shadow is visible, else false
22488      */
22489     isVisible : function(){
22490         return this.el ? true : false;  
22491     },
22492
22493     /**
22494      * Direct alignment when values are already available. Show must be called at least once before
22495      * calling this method to ensure it is initialized.
22496      * @param {Number} left The target element left position
22497      * @param {Number} top The target element top position
22498      * @param {Number} width The target element width
22499      * @param {Number} height The target element height
22500      */
22501     realign : function(l, t, w, h){
22502         if(!this.el){
22503             return;
22504         }
22505         var a = this.adjusts, d = this.el.dom, s = d.style;
22506         var iea = 0;
22507         s.left = (l+a.l)+"px";
22508         s.top = (t+a.t)+"px";
22509         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22510         if(s.width != sws || s.height != shs){
22511             s.width = sws;
22512             s.height = shs;
22513             if(!Roo.isIE){
22514                 var cn = d.childNodes;
22515                 var sww = Math.max(0, (sw-12))+"px";
22516                 cn[0].childNodes[1].style.width = sww;
22517                 cn[1].childNodes[1].style.width = sww;
22518                 cn[2].childNodes[1].style.width = sww;
22519                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22520             }
22521         }
22522     },
22523
22524     /**
22525      * Hides this shadow
22526      */
22527     hide : function(){
22528         if(this.el){
22529             this.el.dom.style.display = "none";
22530             Roo.Shadow.Pool.push(this.el);
22531             delete this.el;
22532         }
22533     },
22534
22535     /**
22536      * Adjust the z-index of this shadow
22537      * @param {Number} zindex The new z-index
22538      */
22539     setZIndex : function(z){
22540         this.zIndex = z;
22541         if(this.el){
22542             this.el.setStyle("z-index", z);
22543         }
22544     }
22545 };
22546
22547 // Private utility class that manages the internal Shadow cache
22548 Roo.Shadow.Pool = function(){
22549     var p = [];
22550     var markup = Roo.isIE ?
22551                  '<div class="x-ie-shadow"></div>' :
22552                  '<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>';
22553     return {
22554         pull : function(){
22555             var sh = p.shift();
22556             if(!sh){
22557                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22558                 sh.autoBoxAdjust = false;
22559             }
22560             return sh;
22561         },
22562
22563         push : function(sh){
22564             p.push(sh);
22565         }
22566     };
22567 }();/*
22568  * Based on:
22569  * Ext JS Library 1.1.1
22570  * Copyright(c) 2006-2007, Ext JS, LLC.
22571  *
22572  * Originally Released Under LGPL - original licence link has changed is not relivant.
22573  *
22574  * Fork - LGPL
22575  * <script type="text/javascript">
22576  */
22577
22578 /**
22579  * @class Roo.BoxComponent
22580  * @extends Roo.Component
22581  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22582  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22583  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22584  * layout containers.
22585  * @constructor
22586  * @param {Roo.Element/String/Object} config The configuration options.
22587  */
22588 Roo.BoxComponent = function(config){
22589     Roo.Component.call(this, config);
22590     this.addEvents({
22591         /**
22592          * @event resize
22593          * Fires after the component is resized.
22594              * @param {Roo.Component} this
22595              * @param {Number} adjWidth The box-adjusted width that was set
22596              * @param {Number} adjHeight The box-adjusted height that was set
22597              * @param {Number} rawWidth The width that was originally specified
22598              * @param {Number} rawHeight The height that was originally specified
22599              */
22600         resize : true,
22601         /**
22602          * @event move
22603          * Fires after the component is moved.
22604              * @param {Roo.Component} this
22605              * @param {Number} x The new x position
22606              * @param {Number} y The new y position
22607              */
22608         move : true
22609     });
22610 };
22611
22612 Roo.extend(Roo.BoxComponent, Roo.Component, {
22613     // private, set in afterRender to signify that the component has been rendered
22614     boxReady : false,
22615     // private, used to defer height settings to subclasses
22616     deferHeight: false,
22617     /** @cfg {Number} width
22618      * width (optional) size of component
22619      */
22620      /** @cfg {Number} height
22621      * height (optional) size of component
22622      */
22623      
22624     /**
22625      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22626      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22627      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22628      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22629      * @return {Roo.BoxComponent} this
22630      */
22631     setSize : function(w, h){
22632         // support for standard size objects
22633         if(typeof w == 'object'){
22634             h = w.height;
22635             w = w.width;
22636         }
22637         // not rendered
22638         if(!this.boxReady){
22639             this.width = w;
22640             this.height = h;
22641             return this;
22642         }
22643
22644         // prevent recalcs when not needed
22645         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22646             return this;
22647         }
22648         this.lastSize = {width: w, height: h};
22649
22650         var adj = this.adjustSize(w, h);
22651         var aw = adj.width, ah = adj.height;
22652         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22653             var rz = this.getResizeEl();
22654             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22655                 rz.setSize(aw, ah);
22656             }else if(!this.deferHeight && ah !== undefined){
22657                 rz.setHeight(ah);
22658             }else if(aw !== undefined){
22659                 rz.setWidth(aw);
22660             }
22661             this.onResize(aw, ah, w, h);
22662             this.fireEvent('resize', this, aw, ah, w, h);
22663         }
22664         return this;
22665     },
22666
22667     /**
22668      * Gets the current size of the component's underlying element.
22669      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22670      */
22671     getSize : function(){
22672         return this.el.getSize();
22673     },
22674
22675     /**
22676      * Gets the current XY position of the component's underlying element.
22677      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22678      * @return {Array} The XY position of the element (e.g., [100, 200])
22679      */
22680     getPosition : function(local){
22681         if(local === true){
22682             return [this.el.getLeft(true), this.el.getTop(true)];
22683         }
22684         return this.xy || this.el.getXY();
22685     },
22686
22687     /**
22688      * Gets the current box measurements of the component's underlying element.
22689      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22690      * @returns {Object} box An object in the format {x, y, width, height}
22691      */
22692     getBox : function(local){
22693         var s = this.el.getSize();
22694         if(local){
22695             s.x = this.el.getLeft(true);
22696             s.y = this.el.getTop(true);
22697         }else{
22698             var xy = this.xy || this.el.getXY();
22699             s.x = xy[0];
22700             s.y = xy[1];
22701         }
22702         return s;
22703     },
22704
22705     /**
22706      * Sets the current box measurements of the component's underlying element.
22707      * @param {Object} box An object in the format {x, y, width, height}
22708      * @returns {Roo.BoxComponent} this
22709      */
22710     updateBox : function(box){
22711         this.setSize(box.width, box.height);
22712         this.setPagePosition(box.x, box.y);
22713         return this;
22714     },
22715
22716     // protected
22717     getResizeEl : function(){
22718         return this.resizeEl || this.el;
22719     },
22720
22721     // protected
22722     getPositionEl : function(){
22723         return this.positionEl || this.el;
22724     },
22725
22726     /**
22727      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22728      * This method fires the move event.
22729      * @param {Number} left The new left
22730      * @param {Number} top The new top
22731      * @returns {Roo.BoxComponent} this
22732      */
22733     setPosition : function(x, y){
22734         this.x = x;
22735         this.y = y;
22736         if(!this.boxReady){
22737             return this;
22738         }
22739         var adj = this.adjustPosition(x, y);
22740         var ax = adj.x, ay = adj.y;
22741
22742         var el = this.getPositionEl();
22743         if(ax !== undefined || ay !== undefined){
22744             if(ax !== undefined && ay !== undefined){
22745                 el.setLeftTop(ax, ay);
22746             }else if(ax !== undefined){
22747                 el.setLeft(ax);
22748             }else if(ay !== undefined){
22749                 el.setTop(ay);
22750             }
22751             this.onPosition(ax, ay);
22752             this.fireEvent('move', this, ax, ay);
22753         }
22754         return this;
22755     },
22756
22757     /**
22758      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22759      * This method fires the move event.
22760      * @param {Number} x The new x position
22761      * @param {Number} y The new y position
22762      * @returns {Roo.BoxComponent} this
22763      */
22764     setPagePosition : function(x, y){
22765         this.pageX = x;
22766         this.pageY = y;
22767         if(!this.boxReady){
22768             return;
22769         }
22770         if(x === undefined || y === undefined){ // cannot translate undefined points
22771             return;
22772         }
22773         var p = this.el.translatePoints(x, y);
22774         this.setPosition(p.left, p.top);
22775         return this;
22776     },
22777
22778     // private
22779     onRender : function(ct, position){
22780         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22781         if(this.resizeEl){
22782             this.resizeEl = Roo.get(this.resizeEl);
22783         }
22784         if(this.positionEl){
22785             this.positionEl = Roo.get(this.positionEl);
22786         }
22787     },
22788
22789     // private
22790     afterRender : function(){
22791         Roo.BoxComponent.superclass.afterRender.call(this);
22792         this.boxReady = true;
22793         this.setSize(this.width, this.height);
22794         if(this.x || this.y){
22795             this.setPosition(this.x, this.y);
22796         }
22797         if(this.pageX || this.pageY){
22798             this.setPagePosition(this.pageX, this.pageY);
22799         }
22800     },
22801
22802     /**
22803      * Force the component's size to recalculate based on the underlying element's current height and width.
22804      * @returns {Roo.BoxComponent} this
22805      */
22806     syncSize : function(){
22807         delete this.lastSize;
22808         this.setSize(this.el.getWidth(), this.el.getHeight());
22809         return this;
22810     },
22811
22812     /**
22813      * Called after the component is resized, this method is empty by default but can be implemented by any
22814      * subclass that needs to perform custom logic after a resize occurs.
22815      * @param {Number} adjWidth The box-adjusted width that was set
22816      * @param {Number} adjHeight The box-adjusted height that was set
22817      * @param {Number} rawWidth The width that was originally specified
22818      * @param {Number} rawHeight The height that was originally specified
22819      */
22820     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22821
22822     },
22823
22824     /**
22825      * Called after the component is moved, this method is empty by default but can be implemented by any
22826      * subclass that needs to perform custom logic after a move occurs.
22827      * @param {Number} x The new x position
22828      * @param {Number} y The new y position
22829      */
22830     onPosition : function(x, y){
22831
22832     },
22833
22834     // private
22835     adjustSize : function(w, h){
22836         if(this.autoWidth){
22837             w = 'auto';
22838         }
22839         if(this.autoHeight){
22840             h = 'auto';
22841         }
22842         return {width : w, height: h};
22843     },
22844
22845     // private
22846     adjustPosition : function(x, y){
22847         return {x : x, y: y};
22848     }
22849 });/*
22850  * Based on:
22851  * Ext JS Library 1.1.1
22852  * Copyright(c) 2006-2007, Ext JS, LLC.
22853  *
22854  * Originally Released Under LGPL - original licence link has changed is not relivant.
22855  *
22856  * Fork - LGPL
22857  * <script type="text/javascript">
22858  */
22859
22860
22861 /**
22862  * @class Roo.SplitBar
22863  * @extends Roo.util.Observable
22864  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22865  * <br><br>
22866  * Usage:
22867  * <pre><code>
22868 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22869                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22870 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22871 split.minSize = 100;
22872 split.maxSize = 600;
22873 split.animate = true;
22874 split.on('moved', splitterMoved);
22875 </code></pre>
22876  * @constructor
22877  * Create a new SplitBar
22878  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22879  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22880  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22881  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22882                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22883                         position of the SplitBar).
22884  */
22885 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22886     
22887     /** @private */
22888     this.el = Roo.get(dragElement, true);
22889     this.el.dom.unselectable = "on";
22890     /** @private */
22891     this.resizingEl = Roo.get(resizingElement, true);
22892
22893     /**
22894      * @private
22895      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22896      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22897      * @type Number
22898      */
22899     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22900     
22901     /**
22902      * The minimum size of the resizing element. (Defaults to 0)
22903      * @type Number
22904      */
22905     this.minSize = 0;
22906     
22907     /**
22908      * The maximum size of the resizing element. (Defaults to 2000)
22909      * @type Number
22910      */
22911     this.maxSize = 2000;
22912     
22913     /**
22914      * Whether to animate the transition to the new size
22915      * @type Boolean
22916      */
22917     this.animate = false;
22918     
22919     /**
22920      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22921      * @type Boolean
22922      */
22923     this.useShim = false;
22924     
22925     /** @private */
22926     this.shim = null;
22927     
22928     if(!existingProxy){
22929         /** @private */
22930         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22931     }else{
22932         this.proxy = Roo.get(existingProxy).dom;
22933     }
22934     /** @private */
22935     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22936     
22937     /** @private */
22938     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22939     
22940     /** @private */
22941     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22942     
22943     /** @private */
22944     this.dragSpecs = {};
22945     
22946     /**
22947      * @private The adapter to use to positon and resize elements
22948      */
22949     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22950     this.adapter.init(this);
22951     
22952     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22953         /** @private */
22954         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22955         this.el.addClass("x-splitbar-h");
22956     }else{
22957         /** @private */
22958         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22959         this.el.addClass("x-splitbar-v");
22960     }
22961     
22962     this.addEvents({
22963         /**
22964          * @event resize
22965          * Fires when the splitter is moved (alias for {@link #event-moved})
22966          * @param {Roo.SplitBar} this
22967          * @param {Number} newSize the new width or height
22968          */
22969         "resize" : true,
22970         /**
22971          * @event moved
22972          * Fires when the splitter is moved
22973          * @param {Roo.SplitBar} this
22974          * @param {Number} newSize the new width or height
22975          */
22976         "moved" : true,
22977         /**
22978          * @event beforeresize
22979          * Fires before the splitter is dragged
22980          * @param {Roo.SplitBar} this
22981          */
22982         "beforeresize" : true,
22983
22984         "beforeapply" : true
22985     });
22986
22987     Roo.util.Observable.call(this);
22988 };
22989
22990 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22991     onStartProxyDrag : function(x, y){
22992         this.fireEvent("beforeresize", this);
22993         if(!this.overlay){
22994             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22995             o.unselectable();
22996             o.enableDisplayMode("block");
22997             // all splitbars share the same overlay
22998             Roo.SplitBar.prototype.overlay = o;
22999         }
23000         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23001         this.overlay.show();
23002         Roo.get(this.proxy).setDisplayed("block");
23003         var size = this.adapter.getElementSize(this);
23004         this.activeMinSize = this.getMinimumSize();;
23005         this.activeMaxSize = this.getMaximumSize();;
23006         var c1 = size - this.activeMinSize;
23007         var c2 = Math.max(this.activeMaxSize - size, 0);
23008         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23009             this.dd.resetConstraints();
23010             this.dd.setXConstraint(
23011                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23012                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23013             );
23014             this.dd.setYConstraint(0, 0);
23015         }else{
23016             this.dd.resetConstraints();
23017             this.dd.setXConstraint(0, 0);
23018             this.dd.setYConstraint(
23019                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23020                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23021             );
23022          }
23023         this.dragSpecs.startSize = size;
23024         this.dragSpecs.startPoint = [x, y];
23025         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23026     },
23027     
23028     /** 
23029      * @private Called after the drag operation by the DDProxy
23030      */
23031     onEndProxyDrag : function(e){
23032         Roo.get(this.proxy).setDisplayed(false);
23033         var endPoint = Roo.lib.Event.getXY(e);
23034         if(this.overlay){
23035             this.overlay.hide();
23036         }
23037         var newSize;
23038         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23039             newSize = this.dragSpecs.startSize + 
23040                 (this.placement == Roo.SplitBar.LEFT ?
23041                     endPoint[0] - this.dragSpecs.startPoint[0] :
23042                     this.dragSpecs.startPoint[0] - endPoint[0]
23043                 );
23044         }else{
23045             newSize = this.dragSpecs.startSize + 
23046                 (this.placement == Roo.SplitBar.TOP ?
23047                     endPoint[1] - this.dragSpecs.startPoint[1] :
23048                     this.dragSpecs.startPoint[1] - endPoint[1]
23049                 );
23050         }
23051         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23052         if(newSize != this.dragSpecs.startSize){
23053             if(this.fireEvent('beforeapply', this, newSize) !== false){
23054                 this.adapter.setElementSize(this, newSize);
23055                 this.fireEvent("moved", this, newSize);
23056                 this.fireEvent("resize", this, newSize);
23057             }
23058         }
23059     },
23060     
23061     /**
23062      * Get the adapter this SplitBar uses
23063      * @return The adapter object
23064      */
23065     getAdapter : function(){
23066         return this.adapter;
23067     },
23068     
23069     /**
23070      * Set the adapter this SplitBar uses
23071      * @param {Object} adapter A SplitBar adapter object
23072      */
23073     setAdapter : function(adapter){
23074         this.adapter = adapter;
23075         this.adapter.init(this);
23076     },
23077     
23078     /**
23079      * Gets the minimum size for the resizing element
23080      * @return {Number} The minimum size
23081      */
23082     getMinimumSize : function(){
23083         return this.minSize;
23084     },
23085     
23086     /**
23087      * Sets the minimum size for the resizing element
23088      * @param {Number} minSize The minimum size
23089      */
23090     setMinimumSize : function(minSize){
23091         this.minSize = minSize;
23092     },
23093     
23094     /**
23095      * Gets the maximum size for the resizing element
23096      * @return {Number} The maximum size
23097      */
23098     getMaximumSize : function(){
23099         return this.maxSize;
23100     },
23101     
23102     /**
23103      * Sets the maximum size for the resizing element
23104      * @param {Number} maxSize The maximum size
23105      */
23106     setMaximumSize : function(maxSize){
23107         this.maxSize = maxSize;
23108     },
23109     
23110     /**
23111      * Sets the initialize size for the resizing element
23112      * @param {Number} size The initial size
23113      */
23114     setCurrentSize : function(size){
23115         var oldAnimate = this.animate;
23116         this.animate = false;
23117         this.adapter.setElementSize(this, size);
23118         this.animate = oldAnimate;
23119     },
23120     
23121     /**
23122      * Destroy this splitbar. 
23123      * @param {Boolean} removeEl True to remove the element
23124      */
23125     destroy : function(removeEl){
23126         if(this.shim){
23127             this.shim.remove();
23128         }
23129         this.dd.unreg();
23130         this.proxy.parentNode.removeChild(this.proxy);
23131         if(removeEl){
23132             this.el.remove();
23133         }
23134     }
23135 });
23136
23137 /**
23138  * @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.
23139  */
23140 Roo.SplitBar.createProxy = function(dir){
23141     var proxy = new Roo.Element(document.createElement("div"));
23142     proxy.unselectable();
23143     var cls = 'x-splitbar-proxy';
23144     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23145     document.body.appendChild(proxy.dom);
23146     return proxy.dom;
23147 };
23148
23149 /** 
23150  * @class Roo.SplitBar.BasicLayoutAdapter
23151  * Default Adapter. It assumes the splitter and resizing element are not positioned
23152  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23153  */
23154 Roo.SplitBar.BasicLayoutAdapter = function(){
23155 };
23156
23157 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23158     // do nothing for now
23159     init : function(s){
23160     
23161     },
23162     /**
23163      * Called before drag operations to get the current size of the resizing element. 
23164      * @param {Roo.SplitBar} s The SplitBar using this adapter
23165      */
23166      getElementSize : function(s){
23167         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23168             return s.resizingEl.getWidth();
23169         }else{
23170             return s.resizingEl.getHeight();
23171         }
23172     },
23173     
23174     /**
23175      * Called after drag operations to set the size of the resizing element.
23176      * @param {Roo.SplitBar} s The SplitBar using this adapter
23177      * @param {Number} newSize The new size to set
23178      * @param {Function} onComplete A function to be invoked when resizing is complete
23179      */
23180     setElementSize : function(s, newSize, onComplete){
23181         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23182             if(!s.animate){
23183                 s.resizingEl.setWidth(newSize);
23184                 if(onComplete){
23185                     onComplete(s, newSize);
23186                 }
23187             }else{
23188                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23189             }
23190         }else{
23191             
23192             if(!s.animate){
23193                 s.resizingEl.setHeight(newSize);
23194                 if(onComplete){
23195                     onComplete(s, newSize);
23196                 }
23197             }else{
23198                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23199             }
23200         }
23201     }
23202 };
23203
23204 /** 
23205  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23206  * @extends Roo.SplitBar.BasicLayoutAdapter
23207  * Adapter that  moves the splitter element to align with the resized sizing element. 
23208  * Used with an absolute positioned SplitBar.
23209  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23210  * document.body, make sure you assign an id to the body element.
23211  */
23212 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23213     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23214     this.container = Roo.get(container);
23215 };
23216
23217 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23218     init : function(s){
23219         this.basic.init(s);
23220     },
23221     
23222     getElementSize : function(s){
23223         return this.basic.getElementSize(s);
23224     },
23225     
23226     setElementSize : function(s, newSize, onComplete){
23227         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23228     },
23229     
23230     moveSplitter : function(s){
23231         var yes = Roo.SplitBar;
23232         switch(s.placement){
23233             case yes.LEFT:
23234                 s.el.setX(s.resizingEl.getRight());
23235                 break;
23236             case yes.RIGHT:
23237                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23238                 break;
23239             case yes.TOP:
23240                 s.el.setY(s.resizingEl.getBottom());
23241                 break;
23242             case yes.BOTTOM:
23243                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23244                 break;
23245         }
23246     }
23247 };
23248
23249 /**
23250  * Orientation constant - Create a vertical SplitBar
23251  * @static
23252  * @type Number
23253  */
23254 Roo.SplitBar.VERTICAL = 1;
23255
23256 /**
23257  * Orientation constant - Create a horizontal SplitBar
23258  * @static
23259  * @type Number
23260  */
23261 Roo.SplitBar.HORIZONTAL = 2;
23262
23263 /**
23264  * Placement constant - The resizing element is to the left of the splitter element
23265  * @static
23266  * @type Number
23267  */
23268 Roo.SplitBar.LEFT = 1;
23269
23270 /**
23271  * Placement constant - The resizing element is to the right of the splitter element
23272  * @static
23273  * @type Number
23274  */
23275 Roo.SplitBar.RIGHT = 2;
23276
23277 /**
23278  * Placement constant - The resizing element is positioned above the splitter element
23279  * @static
23280  * @type Number
23281  */
23282 Roo.SplitBar.TOP = 3;
23283
23284 /**
23285  * Placement constant - The resizing element is positioned under splitter element
23286  * @static
23287  * @type Number
23288  */
23289 Roo.SplitBar.BOTTOM = 4;
23290 /*
23291  * Based on:
23292  * Ext JS Library 1.1.1
23293  * Copyright(c) 2006-2007, Ext JS, LLC.
23294  *
23295  * Originally Released Under LGPL - original licence link has changed is not relivant.
23296  *
23297  * Fork - LGPL
23298  * <script type="text/javascript">
23299  */
23300
23301 /**
23302  * @class Roo.View
23303  * @extends Roo.util.Observable
23304  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23305  * This class also supports single and multi selection modes. <br>
23306  * Create a data model bound view:
23307  <pre><code>
23308  var store = new Roo.data.Store(...);
23309
23310  var view = new Roo.View({
23311     el : "my-element",
23312     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23313  
23314     singleSelect: true,
23315     selectedClass: "ydataview-selected",
23316     store: store
23317  });
23318
23319  // listen for node click?
23320  view.on("click", function(vw, index, node, e){
23321  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23322  });
23323
23324  // load XML data
23325  dataModel.load("foobar.xml");
23326  </code></pre>
23327  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23328  * <br><br>
23329  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23330  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23331  * 
23332  * Note: old style constructor is still suported (container, template, config)
23333  * 
23334  * @constructor
23335  * Create a new View
23336  * @param {Object} config The config object
23337  * 
23338  */
23339 Roo.View = function(config, depreciated_tpl, depreciated_config){
23340     
23341     if (typeof(depreciated_tpl) == 'undefined') {
23342         // new way.. - universal constructor.
23343         Roo.apply(this, config);
23344         this.el  = Roo.get(this.el);
23345     } else {
23346         // old format..
23347         this.el  = Roo.get(config);
23348         this.tpl = depreciated_tpl;
23349         Roo.apply(this, depreciated_config);
23350     }
23351      
23352     
23353     if(typeof(this.tpl) == "string"){
23354         this.tpl = new Roo.Template(this.tpl);
23355     } else {
23356         // support xtype ctors..
23357         this.tpl = new Roo.factory(this.tpl, Roo);
23358     }
23359     
23360     
23361     this.tpl.compile();
23362    
23363
23364      
23365     /** @private */
23366     this.addEvents({
23367     /**
23368      * @event beforeclick
23369      * Fires before a click is processed. Returns false to cancel the default action.
23370      * @param {Roo.View} this
23371      * @param {Number} index The index of the target node
23372      * @param {HTMLElement} node The target node
23373      * @param {Roo.EventObject} e The raw event object
23374      */
23375         "beforeclick" : true,
23376     /**
23377      * @event click
23378      * Fires when a template node is clicked.
23379      * @param {Roo.View} this
23380      * @param {Number} index The index of the target node
23381      * @param {HTMLElement} node The target node
23382      * @param {Roo.EventObject} e The raw event object
23383      */
23384         "click" : true,
23385     /**
23386      * @event dblclick
23387      * Fires when a template node is double clicked.
23388      * @param {Roo.View} this
23389      * @param {Number} index The index of the target node
23390      * @param {HTMLElement} node The target node
23391      * @param {Roo.EventObject} e The raw event object
23392      */
23393         "dblclick" : true,
23394     /**
23395      * @event contextmenu
23396      * Fires when a template node is right clicked.
23397      * @param {Roo.View} this
23398      * @param {Number} index The index of the target node
23399      * @param {HTMLElement} node The target node
23400      * @param {Roo.EventObject} e The raw event object
23401      */
23402         "contextmenu" : true,
23403     /**
23404      * @event selectionchange
23405      * Fires when the selected nodes change.
23406      * @param {Roo.View} this
23407      * @param {Array} selections Array of the selected nodes
23408      */
23409         "selectionchange" : true,
23410
23411     /**
23412      * @event beforeselect
23413      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23414      * @param {Roo.View} this
23415      * @param {HTMLElement} node The node to be selected
23416      * @param {Array} selections Array of currently selected nodes
23417      */
23418         "beforeselect" : true
23419     });
23420
23421     this.el.on({
23422         "click": this.onClick,
23423         "dblclick": this.onDblClick,
23424         "contextmenu": this.onContextMenu,
23425         scope:this
23426     });
23427
23428     this.selections = [];
23429     this.nodes = [];
23430     this.cmp = new Roo.CompositeElementLite([]);
23431     if(this.store){
23432         this.store = Roo.factory(this.store, Roo.data);
23433         this.setStore(this.store, true);
23434     }
23435     Roo.View.superclass.constructor.call(this);
23436 };
23437
23438 Roo.extend(Roo.View, Roo.util.Observable, {
23439     
23440      /**
23441      * @cfg {Roo.data.Store} store Data store to load data from.
23442      */
23443     store : false,
23444     
23445     /**
23446      * @cfg {String|Roo.Element} el The container element.
23447      */
23448     el : '',
23449     
23450     /**
23451      * @cfg {String|Roo.Template} tpl The template used by this View 
23452      */
23453     tpl : false,
23454     
23455     /**
23456      * @cfg {String} selectedClass The css class to add to selected nodes
23457      */
23458     selectedClass : "x-view-selected",
23459      /**
23460      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23461      */
23462     emptyText : "",
23463     /**
23464      * @cfg {Boolean} multiSelect Allow multiple selection
23465      */
23466     
23467     multiSelect : false,
23468     /**
23469      * @cfg {Boolean} singleSelect Allow single selection
23470      */
23471     singleSelect:  false,
23472     
23473     /**
23474      * Returns the element this view is bound to.
23475      * @return {Roo.Element}
23476      */
23477     getEl : function(){
23478         return this.el;
23479     },
23480
23481     /**
23482      * Refreshes the view.
23483      */
23484     refresh : function(){
23485         var t = this.tpl;
23486         this.clearSelections();
23487         this.el.update("");
23488         var html = [];
23489         var records = this.store.getRange();
23490         if(records.length < 1){
23491             this.el.update(this.emptyText);
23492             return;
23493         }
23494         for(var i = 0, len = records.length; i < len; i++){
23495             var data = this.prepareData(records[i].data, i, records[i]);
23496             html[html.length] = t.apply(data);
23497         }
23498         this.el.update(html.join(""));
23499         this.nodes = this.el.dom.childNodes;
23500         this.updateIndexes(0);
23501     },
23502
23503     /**
23504      * Function to override to reformat the data that is sent to
23505      * the template for each node.
23506      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23507      * a JSON object for an UpdateManager bound view).
23508      */
23509     prepareData : function(data){
23510         return data;
23511     },
23512
23513     onUpdate : function(ds, record){
23514         this.clearSelections();
23515         var index = this.store.indexOf(record);
23516         var n = this.nodes[index];
23517         this.tpl.insertBefore(n, this.prepareData(record.data));
23518         n.parentNode.removeChild(n);
23519         this.updateIndexes(index, index);
23520     },
23521
23522     onAdd : function(ds, records, index){
23523         this.clearSelections();
23524         if(this.nodes.length == 0){
23525             this.refresh();
23526             return;
23527         }
23528         var n = this.nodes[index];
23529         for(var i = 0, len = records.length; i < len; i++){
23530             var d = this.prepareData(records[i].data);
23531             if(n){
23532                 this.tpl.insertBefore(n, d);
23533             }else{
23534                 this.tpl.append(this.el, d);
23535             }
23536         }
23537         this.updateIndexes(index);
23538     },
23539
23540     onRemove : function(ds, record, index){
23541         this.clearSelections();
23542         this.el.dom.removeChild(this.nodes[index]);
23543         this.updateIndexes(index);
23544     },
23545
23546     /**
23547      * Refresh an individual node.
23548      * @param {Number} index
23549      */
23550     refreshNode : function(index){
23551         this.onUpdate(this.store, this.store.getAt(index));
23552     },
23553
23554     updateIndexes : function(startIndex, endIndex){
23555         var ns = this.nodes;
23556         startIndex = startIndex || 0;
23557         endIndex = endIndex || ns.length - 1;
23558         for(var i = startIndex; i <= endIndex; i++){
23559             ns[i].nodeIndex = i;
23560         }
23561     },
23562
23563     /**
23564      * Changes the data store this view uses and refresh the view.
23565      * @param {Store} store
23566      */
23567     setStore : function(store, initial){
23568         if(!initial && this.store){
23569             this.store.un("datachanged", this.refresh);
23570             this.store.un("add", this.onAdd);
23571             this.store.un("remove", this.onRemove);
23572             this.store.un("update", this.onUpdate);
23573             this.store.un("clear", this.refresh);
23574         }
23575         if(store){
23576           
23577             store.on("datachanged", this.refresh, this);
23578             store.on("add", this.onAdd, this);
23579             store.on("remove", this.onRemove, this);
23580             store.on("update", this.onUpdate, this);
23581             store.on("clear", this.refresh, this);
23582         }
23583         
23584         if(store){
23585             this.refresh();
23586         }
23587     },
23588
23589     /**
23590      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23591      * @param {HTMLElement} node
23592      * @return {HTMLElement} The template node
23593      */
23594     findItemFromChild : function(node){
23595         var el = this.el.dom;
23596         if(!node || node.parentNode == el){
23597                     return node;
23598             }
23599             var p = node.parentNode;
23600             while(p && p != el){
23601             if(p.parentNode == el){
23602                 return p;
23603             }
23604             p = p.parentNode;
23605         }
23606             return null;
23607     },
23608
23609     /** @ignore */
23610     onClick : function(e){
23611         var item = this.findItemFromChild(e.getTarget());
23612         if(item){
23613             var index = this.indexOf(item);
23614             if(this.onItemClick(item, index, e) !== false){
23615                 this.fireEvent("click", this, index, item, e);
23616             }
23617         }else{
23618             this.clearSelections();
23619         }
23620     },
23621
23622     /** @ignore */
23623     onContextMenu : function(e){
23624         var item = this.findItemFromChild(e.getTarget());
23625         if(item){
23626             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23627         }
23628     },
23629
23630     /** @ignore */
23631     onDblClick : function(e){
23632         var item = this.findItemFromChild(e.getTarget());
23633         if(item){
23634             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23635         }
23636     },
23637
23638     onItemClick : function(item, index, e){
23639         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23640             return false;
23641         }
23642         if(this.multiSelect || this.singleSelect){
23643             if(this.multiSelect && e.shiftKey && this.lastSelection){
23644                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23645             }else{
23646                 this.select(item, this.multiSelect && e.ctrlKey);
23647                 this.lastSelection = item;
23648             }
23649             e.preventDefault();
23650         }
23651         return true;
23652     },
23653
23654     /**
23655      * Get the number of selected nodes.
23656      * @return {Number}
23657      */
23658     getSelectionCount : function(){
23659         return this.selections.length;
23660     },
23661
23662     /**
23663      * Get the currently selected nodes.
23664      * @return {Array} An array of HTMLElements
23665      */
23666     getSelectedNodes : function(){
23667         return this.selections;
23668     },
23669
23670     /**
23671      * Get the indexes of the selected nodes.
23672      * @return {Array}
23673      */
23674     getSelectedIndexes : function(){
23675         var indexes = [], s = this.selections;
23676         for(var i = 0, len = s.length; i < len; i++){
23677             indexes.push(s[i].nodeIndex);
23678         }
23679         return indexes;
23680     },
23681
23682     /**
23683      * Clear all selections
23684      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23685      */
23686     clearSelections : function(suppressEvent){
23687         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23688             this.cmp.elements = this.selections;
23689             this.cmp.removeClass(this.selectedClass);
23690             this.selections = [];
23691             if(!suppressEvent){
23692                 this.fireEvent("selectionchange", this, this.selections);
23693             }
23694         }
23695     },
23696
23697     /**
23698      * Returns true if the passed node is selected
23699      * @param {HTMLElement/Number} node The node or node index
23700      * @return {Boolean}
23701      */
23702     isSelected : function(node){
23703         var s = this.selections;
23704         if(s.length < 1){
23705             return false;
23706         }
23707         node = this.getNode(node);
23708         return s.indexOf(node) !== -1;
23709     },
23710
23711     /**
23712      * Selects nodes.
23713      * @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
23714      * @param {Boolean} keepExisting (optional) true to keep existing selections
23715      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23716      */
23717     select : function(nodeInfo, keepExisting, suppressEvent){
23718         if(nodeInfo instanceof Array){
23719             if(!keepExisting){
23720                 this.clearSelections(true);
23721             }
23722             for(var i = 0, len = nodeInfo.length; i < len; i++){
23723                 this.select(nodeInfo[i], true, true);
23724             }
23725         } else{
23726             var node = this.getNode(nodeInfo);
23727             if(node && !this.isSelected(node)){
23728                 if(!keepExisting){
23729                     this.clearSelections(true);
23730                 }
23731                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23732                     Roo.fly(node).addClass(this.selectedClass);
23733                     this.selections.push(node);
23734                     if(!suppressEvent){
23735                         this.fireEvent("selectionchange", this, this.selections);
23736                     }
23737                 }
23738             }
23739         }
23740     },
23741
23742     /**
23743      * Gets a template node.
23744      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23745      * @return {HTMLElement} The node or null if it wasn't found
23746      */
23747     getNode : function(nodeInfo){
23748         if(typeof nodeInfo == "string"){
23749             return document.getElementById(nodeInfo);
23750         }else if(typeof nodeInfo == "number"){
23751             return this.nodes[nodeInfo];
23752         }
23753         return nodeInfo;
23754     },
23755
23756     /**
23757      * Gets a range template nodes.
23758      * @param {Number} startIndex
23759      * @param {Number} endIndex
23760      * @return {Array} An array of nodes
23761      */
23762     getNodes : function(start, end){
23763         var ns = this.nodes;
23764         start = start || 0;
23765         end = typeof end == "undefined" ? ns.length - 1 : end;
23766         var nodes = [];
23767         if(start <= end){
23768             for(var i = start; i <= end; i++){
23769                 nodes.push(ns[i]);
23770             }
23771         } else{
23772             for(var i = start; i >= end; i--){
23773                 nodes.push(ns[i]);
23774             }
23775         }
23776         return nodes;
23777     },
23778
23779     /**
23780      * Finds the index of the passed node
23781      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23782      * @return {Number} The index of the node or -1
23783      */
23784     indexOf : function(node){
23785         node = this.getNode(node);
23786         if(typeof node.nodeIndex == "number"){
23787             return node.nodeIndex;
23788         }
23789         var ns = this.nodes;
23790         for(var i = 0, len = ns.length; i < len; i++){
23791             if(ns[i] == node){
23792                 return i;
23793             }
23794         }
23795         return -1;
23796     }
23797 });
23798 /*
23799  * Based on:
23800  * Ext JS Library 1.1.1
23801  * Copyright(c) 2006-2007, Ext JS, LLC.
23802  *
23803  * Originally Released Under LGPL - original licence link has changed is not relivant.
23804  *
23805  * Fork - LGPL
23806  * <script type="text/javascript">
23807  */
23808
23809 /**
23810  * @class Roo.JsonView
23811  * @extends Roo.View
23812  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23813 <pre><code>
23814 var view = new Roo.JsonView({
23815     container: "my-element",
23816     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23817     multiSelect: true, 
23818     jsonRoot: "data" 
23819 });
23820
23821 // listen for node click?
23822 view.on("click", function(vw, index, node, e){
23823     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23824 });
23825
23826 // direct load of JSON data
23827 view.load("foobar.php");
23828
23829 // Example from my blog list
23830 var tpl = new Roo.Template(
23831     '&lt;div class="entry"&gt;' +
23832     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23833     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23834     "&lt;/div&gt;&lt;hr /&gt;"
23835 );
23836
23837 var moreView = new Roo.JsonView({
23838     container :  "entry-list", 
23839     template : tpl,
23840     jsonRoot: "posts"
23841 });
23842 moreView.on("beforerender", this.sortEntries, this);
23843 moreView.load({
23844     url: "/blog/get-posts.php",
23845     params: "allposts=true",
23846     text: "Loading Blog Entries..."
23847 });
23848 </code></pre>
23849
23850 * Note: old code is supported with arguments : (container, template, config)
23851
23852
23853  * @constructor
23854  * Create a new JsonView
23855  * 
23856  * @param {Object} config The config object
23857  * 
23858  */
23859 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23860     
23861     
23862     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23863
23864     var um = this.el.getUpdateManager();
23865     um.setRenderer(this);
23866     um.on("update", this.onLoad, this);
23867     um.on("failure", this.onLoadException, this);
23868
23869     /**
23870      * @event beforerender
23871      * Fires before rendering of the downloaded JSON data.
23872      * @param {Roo.JsonView} this
23873      * @param {Object} data The JSON data loaded
23874      */
23875     /**
23876      * @event load
23877      * Fires when data is loaded.
23878      * @param {Roo.JsonView} this
23879      * @param {Object} data The JSON data loaded
23880      * @param {Object} response The raw Connect response object
23881      */
23882     /**
23883      * @event loadexception
23884      * Fires when loading fails.
23885      * @param {Roo.JsonView} this
23886      * @param {Object} response The raw Connect response object
23887      */
23888     this.addEvents({
23889         'beforerender' : true,
23890         'load' : true,
23891         'loadexception' : true
23892     });
23893 };
23894 Roo.extend(Roo.JsonView, Roo.View, {
23895     /**
23896      * @type {String} The root property in the loaded JSON object that contains the data
23897      */
23898     jsonRoot : "",
23899
23900     /**
23901      * Refreshes the view.
23902      */
23903     refresh : function(){
23904         this.clearSelections();
23905         this.el.update("");
23906         var html = [];
23907         var o = this.jsonData;
23908         if(o && o.length > 0){
23909             for(var i = 0, len = o.length; i < len; i++){
23910                 var data = this.prepareData(o[i], i, o);
23911                 html[html.length] = this.tpl.apply(data);
23912             }
23913         }else{
23914             html.push(this.emptyText);
23915         }
23916         this.el.update(html.join(""));
23917         this.nodes = this.el.dom.childNodes;
23918         this.updateIndexes(0);
23919     },
23920
23921     /**
23922      * 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.
23923      * @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:
23924      <pre><code>
23925      view.load({
23926          url: "your-url.php",
23927          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23928          callback: yourFunction,
23929          scope: yourObject, //(optional scope)
23930          discardUrl: false,
23931          nocache: false,
23932          text: "Loading...",
23933          timeout: 30,
23934          scripts: false
23935      });
23936      </code></pre>
23937      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23938      * 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.
23939      * @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}
23940      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23941      * @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.
23942      */
23943     load : function(){
23944         var um = this.el.getUpdateManager();
23945         um.update.apply(um, arguments);
23946     },
23947
23948     render : function(el, response){
23949         this.clearSelections();
23950         this.el.update("");
23951         var o;
23952         try{
23953             o = Roo.util.JSON.decode(response.responseText);
23954             if(this.jsonRoot){
23955                 
23956                 o = o[this.jsonRoot];
23957             }
23958         } catch(e){
23959         }
23960         /**
23961          * The current JSON data or null
23962          */
23963         this.jsonData = o;
23964         this.beforeRender();
23965         this.refresh();
23966     },
23967
23968 /**
23969  * Get the number of records in the current JSON dataset
23970  * @return {Number}
23971  */
23972     getCount : function(){
23973         return this.jsonData ? this.jsonData.length : 0;
23974     },
23975
23976 /**
23977  * Returns the JSON object for the specified node(s)
23978  * @param {HTMLElement/Array} node The node or an array of nodes
23979  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23980  * you get the JSON object for the node
23981  */
23982     getNodeData : function(node){
23983         if(node instanceof Array){
23984             var data = [];
23985             for(var i = 0, len = node.length; i < len; i++){
23986                 data.push(this.getNodeData(node[i]));
23987             }
23988             return data;
23989         }
23990         return this.jsonData[this.indexOf(node)] || null;
23991     },
23992
23993     beforeRender : function(){
23994         this.snapshot = this.jsonData;
23995         if(this.sortInfo){
23996             this.sort.apply(this, this.sortInfo);
23997         }
23998         this.fireEvent("beforerender", this, this.jsonData);
23999     },
24000
24001     onLoad : function(el, o){
24002         this.fireEvent("load", this, this.jsonData, o);
24003     },
24004
24005     onLoadException : function(el, o){
24006         this.fireEvent("loadexception", this, o);
24007     },
24008
24009 /**
24010  * Filter the data by a specific property.
24011  * @param {String} property A property on your JSON objects
24012  * @param {String/RegExp} value Either string that the property values
24013  * should start with, or a RegExp to test against the property
24014  */
24015     filter : function(property, value){
24016         if(this.jsonData){
24017             var data = [];
24018             var ss = this.snapshot;
24019             if(typeof value == "string"){
24020                 var vlen = value.length;
24021                 if(vlen == 0){
24022                     this.clearFilter();
24023                     return;
24024                 }
24025                 value = value.toLowerCase();
24026                 for(var i = 0, len = ss.length; i < len; i++){
24027                     var o = ss[i];
24028                     if(o[property].substr(0, vlen).toLowerCase() == value){
24029                         data.push(o);
24030                     }
24031                 }
24032             } else if(value.exec){ // regex?
24033                 for(var i = 0, len = ss.length; i < len; i++){
24034                     var o = ss[i];
24035                     if(value.test(o[property])){
24036                         data.push(o);
24037                     }
24038                 }
24039             } else{
24040                 return;
24041             }
24042             this.jsonData = data;
24043             this.refresh();
24044         }
24045     },
24046
24047 /**
24048  * Filter by a function. The passed function will be called with each
24049  * object in the current dataset. If the function returns true the value is kept,
24050  * otherwise it is filtered.
24051  * @param {Function} fn
24052  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24053  */
24054     filterBy : function(fn, scope){
24055         if(this.jsonData){
24056             var data = [];
24057             var ss = this.snapshot;
24058             for(var i = 0, len = ss.length; i < len; i++){
24059                 var o = ss[i];
24060                 if(fn.call(scope || this, o)){
24061                     data.push(o);
24062                 }
24063             }
24064             this.jsonData = data;
24065             this.refresh();
24066         }
24067     },
24068
24069 /**
24070  * Clears the current filter.
24071  */
24072     clearFilter : function(){
24073         if(this.snapshot && this.jsonData != this.snapshot){
24074             this.jsonData = this.snapshot;
24075             this.refresh();
24076         }
24077     },
24078
24079
24080 /**
24081  * Sorts the data for this view and refreshes it.
24082  * @param {String} property A property on your JSON objects to sort on
24083  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24084  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24085  */
24086     sort : function(property, dir, sortType){
24087         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24088         if(this.jsonData){
24089             var p = property;
24090             var dsc = dir && dir.toLowerCase() == "desc";
24091             var f = function(o1, o2){
24092                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24093                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24094                 ;
24095                 if(v1 < v2){
24096                     return dsc ? +1 : -1;
24097                 } else if(v1 > v2){
24098                     return dsc ? -1 : +1;
24099                 } else{
24100                     return 0;
24101                 }
24102             };
24103             this.jsonData.sort(f);
24104             this.refresh();
24105             if(this.jsonData != this.snapshot){
24106                 this.snapshot.sort(f);
24107             }
24108         }
24109     }
24110 });/*
24111  * Based on:
24112  * Ext JS Library 1.1.1
24113  * Copyright(c) 2006-2007, Ext JS, LLC.
24114  *
24115  * Originally Released Under LGPL - original licence link has changed is not relivant.
24116  *
24117  * Fork - LGPL
24118  * <script type="text/javascript">
24119  */
24120  
24121
24122 /**
24123  * @class Roo.ColorPalette
24124  * @extends Roo.Component
24125  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24126  * Here's an example of typical usage:
24127  * <pre><code>
24128 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24129 cp.render('my-div');
24130
24131 cp.on('select', function(palette, selColor){
24132     // do something with selColor
24133 });
24134 </code></pre>
24135  * @constructor
24136  * Create a new ColorPalette
24137  * @param {Object} config The config object
24138  */
24139 Roo.ColorPalette = function(config){
24140     Roo.ColorPalette.superclass.constructor.call(this, config);
24141     this.addEvents({
24142         /**
24143              * @event select
24144              * Fires when a color is selected
24145              * @param {ColorPalette} this
24146              * @param {String} color The 6-digit color hex code (without the # symbol)
24147              */
24148         select: true
24149     });
24150
24151     if(this.handler){
24152         this.on("select", this.handler, this.scope, true);
24153     }
24154 };
24155 Roo.extend(Roo.ColorPalette, Roo.Component, {
24156     /**
24157      * @cfg {String} itemCls
24158      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24159      */
24160     itemCls : "x-color-palette",
24161     /**
24162      * @cfg {String} value
24163      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24164      * the hex codes are case-sensitive.
24165      */
24166     value : null,
24167     clickEvent:'click',
24168     // private
24169     ctype: "Roo.ColorPalette",
24170
24171     /**
24172      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24173      */
24174     allowReselect : false,
24175
24176     /**
24177      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24178      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24179      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24180      * of colors with the width setting until the box is symmetrical.</p>
24181      * <p>You can override individual colors if needed:</p>
24182      * <pre><code>
24183 var cp = new Roo.ColorPalette();
24184 cp.colors[0] = "FF0000";  // change the first box to red
24185 </code></pre>
24186
24187 Or you can provide a custom array of your own for complete control:
24188 <pre><code>
24189 var cp = new Roo.ColorPalette();
24190 cp.colors = ["000000", "993300", "333300"];
24191 </code></pre>
24192      * @type Array
24193      */
24194     colors : [
24195         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24196         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24197         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24198         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24199         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24200     ],
24201
24202     // private
24203     onRender : function(container, position){
24204         var t = new Roo.MasterTemplate(
24205             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24206         );
24207         var c = this.colors;
24208         for(var i = 0, len = c.length; i < len; i++){
24209             t.add([c[i]]);
24210         }
24211         var el = document.createElement("div");
24212         el.className = this.itemCls;
24213         t.overwrite(el);
24214         container.dom.insertBefore(el, position);
24215         this.el = Roo.get(el);
24216         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24217         if(this.clickEvent != 'click'){
24218             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24219         }
24220     },
24221
24222     // private
24223     afterRender : function(){
24224         Roo.ColorPalette.superclass.afterRender.call(this);
24225         if(this.value){
24226             var s = this.value;
24227             this.value = null;
24228             this.select(s);
24229         }
24230     },
24231
24232     // private
24233     handleClick : function(e, t){
24234         e.preventDefault();
24235         if(!this.disabled){
24236             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24237             this.select(c.toUpperCase());
24238         }
24239     },
24240
24241     /**
24242      * Selects the specified color in the palette (fires the select event)
24243      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24244      */
24245     select : function(color){
24246         color = color.replace("#", "");
24247         if(color != this.value || this.allowReselect){
24248             var el = this.el;
24249             if(this.value){
24250                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24251             }
24252             el.child("a.color-"+color).addClass("x-color-palette-sel");
24253             this.value = color;
24254             this.fireEvent("select", this, color);
24255         }
24256     }
24257 });/*
24258  * Based on:
24259  * Ext JS Library 1.1.1
24260  * Copyright(c) 2006-2007, Ext JS, LLC.
24261  *
24262  * Originally Released Under LGPL - original licence link has changed is not relivant.
24263  *
24264  * Fork - LGPL
24265  * <script type="text/javascript">
24266  */
24267  
24268 /**
24269  * @class Roo.DatePicker
24270  * @extends Roo.Component
24271  * Simple date picker class.
24272  * @constructor
24273  * Create a new DatePicker
24274  * @param {Object} config The config object
24275  */
24276 Roo.DatePicker = function(config){
24277     Roo.DatePicker.superclass.constructor.call(this, config);
24278
24279     this.value = config && config.value ?
24280                  config.value.clearTime() : new Date().clearTime();
24281
24282     this.addEvents({
24283         /**
24284              * @event select
24285              * Fires when a date is selected
24286              * @param {DatePicker} this
24287              * @param {Date} date The selected date
24288              */
24289         select: true
24290     });
24291
24292     if(this.handler){
24293         this.on("select", this.handler,  this.scope || this);
24294     }
24295     // build the disabledDatesRE
24296     if(!this.disabledDatesRE && this.disabledDates){
24297         var dd = this.disabledDates;
24298         var re = "(?:";
24299         for(var i = 0; i < dd.length; i++){
24300             re += dd[i];
24301             if(i != dd.length-1) re += "|";
24302         }
24303         this.disabledDatesRE = new RegExp(re + ")");
24304     }
24305 };
24306
24307 Roo.extend(Roo.DatePicker, Roo.Component, {
24308     /**
24309      * @cfg {String} todayText
24310      * The text to display on the button that selects the current date (defaults to "Today")
24311      */
24312     todayText : "Today",
24313     /**
24314      * @cfg {String} okText
24315      * The text to display on the ok button
24316      */
24317     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24318     /**
24319      * @cfg {String} cancelText
24320      * The text to display on the cancel button
24321      */
24322     cancelText : "Cancel",
24323     /**
24324      * @cfg {String} todayTip
24325      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24326      */
24327     todayTip : "{0} (Spacebar)",
24328     /**
24329      * @cfg {Date} minDate
24330      * Minimum allowable date (JavaScript date object, defaults to null)
24331      */
24332     minDate : null,
24333     /**
24334      * @cfg {Date} maxDate
24335      * Maximum allowable date (JavaScript date object, defaults to null)
24336      */
24337     maxDate : null,
24338     /**
24339      * @cfg {String} minText
24340      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24341      */
24342     minText : "This date is before the minimum date",
24343     /**
24344      * @cfg {String} maxText
24345      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24346      */
24347     maxText : "This date is after the maximum date",
24348     /**
24349      * @cfg {String} format
24350      * The default date format string which can be overriden for localization support.  The format must be
24351      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24352      */
24353     format : "m/d/y",
24354     /**
24355      * @cfg {Array} disabledDays
24356      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24357      */
24358     disabledDays : null,
24359     /**
24360      * @cfg {String} disabledDaysText
24361      * The tooltip to display when the date falls on a disabled day (defaults to "")
24362      */
24363     disabledDaysText : "",
24364     /**
24365      * @cfg {RegExp} disabledDatesRE
24366      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24367      */
24368     disabledDatesRE : null,
24369     /**
24370      * @cfg {String} disabledDatesText
24371      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24372      */
24373     disabledDatesText : "",
24374     /**
24375      * @cfg {Boolean} constrainToViewport
24376      * True to constrain the date picker to the viewport (defaults to true)
24377      */
24378     constrainToViewport : true,
24379     /**
24380      * @cfg {Array} monthNames
24381      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24382      */
24383     monthNames : Date.monthNames,
24384     /**
24385      * @cfg {Array} dayNames
24386      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24387      */
24388     dayNames : Date.dayNames,
24389     /**
24390      * @cfg {String} nextText
24391      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24392      */
24393     nextText: 'Next Month (Control+Right)',
24394     /**
24395      * @cfg {String} prevText
24396      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24397      */
24398     prevText: 'Previous Month (Control+Left)',
24399     /**
24400      * @cfg {String} monthYearText
24401      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24402      */
24403     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24404     /**
24405      * @cfg {Number} startDay
24406      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24407      */
24408     startDay : 0,
24409     /**
24410      * @cfg {Bool} showClear
24411      * Show a clear button (usefull for date form elements that can be blank.)
24412      */
24413     
24414     showClear: false,
24415     
24416     /**
24417      * Sets the value of the date field
24418      * @param {Date} value The date to set
24419      */
24420     setValue : function(value){
24421         var old = this.value;
24422         this.value = value.clearTime(true);
24423         if(this.el){
24424             this.update(this.value);
24425         }
24426     },
24427
24428     /**
24429      * Gets the current selected value of the date field
24430      * @return {Date} The selected date
24431      */
24432     getValue : function(){
24433         return this.value;
24434     },
24435
24436     // private
24437     focus : function(){
24438         if(this.el){
24439             this.update(this.activeDate);
24440         }
24441     },
24442
24443     // private
24444     onRender : function(container, position){
24445         var m = [
24446              '<table cellspacing="0">',
24447                 '<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>',
24448                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24449         var dn = this.dayNames;
24450         for(var i = 0; i < 7; i++){
24451             var d = this.startDay+i;
24452             if(d > 6){
24453                 d = d-7;
24454             }
24455             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24456         }
24457         m[m.length] = "</tr></thead><tbody><tr>";
24458         for(var i = 0; i < 42; i++) {
24459             if(i % 7 == 0 && i != 0){
24460                 m[m.length] = "</tr><tr>";
24461             }
24462             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24463         }
24464         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24465             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24466
24467         var el = document.createElement("div");
24468         el.className = "x-date-picker";
24469         el.innerHTML = m.join("");
24470
24471         container.dom.insertBefore(el, position);
24472
24473         this.el = Roo.get(el);
24474         this.eventEl = Roo.get(el.firstChild);
24475
24476         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24477             handler: this.showPrevMonth,
24478             scope: this,
24479             preventDefault:true,
24480             stopDefault:true
24481         });
24482
24483         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24484             handler: this.showNextMonth,
24485             scope: this,
24486             preventDefault:true,
24487             stopDefault:true
24488         });
24489
24490         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24491
24492         this.monthPicker = this.el.down('div.x-date-mp');
24493         this.monthPicker.enableDisplayMode('block');
24494         
24495         var kn = new Roo.KeyNav(this.eventEl, {
24496             "left" : function(e){
24497                 e.ctrlKey ?
24498                     this.showPrevMonth() :
24499                     this.update(this.activeDate.add("d", -1));
24500             },
24501
24502             "right" : function(e){
24503                 e.ctrlKey ?
24504                     this.showNextMonth() :
24505                     this.update(this.activeDate.add("d", 1));
24506             },
24507
24508             "up" : function(e){
24509                 e.ctrlKey ?
24510                     this.showNextYear() :
24511                     this.update(this.activeDate.add("d", -7));
24512             },
24513
24514             "down" : function(e){
24515                 e.ctrlKey ?
24516                     this.showPrevYear() :
24517                     this.update(this.activeDate.add("d", 7));
24518             },
24519
24520             "pageUp" : function(e){
24521                 this.showNextMonth();
24522             },
24523
24524             "pageDown" : function(e){
24525                 this.showPrevMonth();
24526             },
24527
24528             "enter" : function(e){
24529                 e.stopPropagation();
24530                 return true;
24531             },
24532
24533             scope : this
24534         });
24535
24536         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24537
24538         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24539
24540         this.el.unselectable();
24541         
24542         this.cells = this.el.select("table.x-date-inner tbody td");
24543         this.textNodes = this.el.query("table.x-date-inner tbody span");
24544
24545         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24546             text: "&#160;",
24547             tooltip: this.monthYearText
24548         });
24549
24550         this.mbtn.on('click', this.showMonthPicker, this);
24551         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24552
24553
24554         var today = (new Date()).dateFormat(this.format);
24555         
24556         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24557         baseTb.add({
24558             text: String.format(this.todayText, today),
24559             tooltip: String.format(this.todayTip, today),
24560             handler: this.selectToday,
24561             scope: this
24562         });
24563         
24564         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24565             
24566         //});
24567         if (this.showClear) {
24568             
24569             baseTb.add( new Roo.Toolbar.Fill());
24570             baseTb.add({
24571                 text: '&#160;',
24572                 cls: 'x-btn-icon x-btn-clear',
24573                 handler: function() {
24574                     //this.value = '';
24575                     this.fireEvent("select", this, '');
24576                 },
24577                 scope: this
24578             });
24579         }
24580         
24581         
24582         if(Roo.isIE){
24583             this.el.repaint();
24584         }
24585         this.update(this.value);
24586     },
24587
24588     createMonthPicker : function(){
24589         if(!this.monthPicker.dom.firstChild){
24590             var buf = ['<table border="0" cellspacing="0">'];
24591             for(var i = 0; i < 6; i++){
24592                 buf.push(
24593                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24594                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24595                     i == 0 ?
24596                     '<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>' :
24597                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24598                 );
24599             }
24600             buf.push(
24601                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24602                     this.okText,
24603                     '</button><button type="button" class="x-date-mp-cancel">',
24604                     this.cancelText,
24605                     '</button></td></tr>',
24606                 '</table>'
24607             );
24608             this.monthPicker.update(buf.join(''));
24609             this.monthPicker.on('click', this.onMonthClick, this);
24610             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24611
24612             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24613             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24614
24615             this.mpMonths.each(function(m, a, i){
24616                 i += 1;
24617                 if((i%2) == 0){
24618                     m.dom.xmonth = 5 + Math.round(i * .5);
24619                 }else{
24620                     m.dom.xmonth = Math.round((i-1) * .5);
24621                 }
24622             });
24623         }
24624     },
24625
24626     showMonthPicker : function(){
24627         this.createMonthPicker();
24628         var size = this.el.getSize();
24629         this.monthPicker.setSize(size);
24630         this.monthPicker.child('table').setSize(size);
24631
24632         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24633         this.updateMPMonth(this.mpSelMonth);
24634         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24635         this.updateMPYear(this.mpSelYear);
24636
24637         this.monthPicker.slideIn('t', {duration:.2});
24638     },
24639
24640     updateMPYear : function(y){
24641         this.mpyear = y;
24642         var ys = this.mpYears.elements;
24643         for(var i = 1; i <= 10; i++){
24644             var td = ys[i-1], y2;
24645             if((i%2) == 0){
24646                 y2 = y + Math.round(i * .5);
24647                 td.firstChild.innerHTML = y2;
24648                 td.xyear = y2;
24649             }else{
24650                 y2 = y - (5-Math.round(i * .5));
24651                 td.firstChild.innerHTML = y2;
24652                 td.xyear = y2;
24653             }
24654             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24655         }
24656     },
24657
24658     updateMPMonth : function(sm){
24659         this.mpMonths.each(function(m, a, i){
24660             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24661         });
24662     },
24663
24664     selectMPMonth: function(m){
24665         
24666     },
24667
24668     onMonthClick : function(e, t){
24669         e.stopEvent();
24670         var el = new Roo.Element(t), pn;
24671         if(el.is('button.x-date-mp-cancel')){
24672             this.hideMonthPicker();
24673         }
24674         else if(el.is('button.x-date-mp-ok')){
24675             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24676             this.hideMonthPicker();
24677         }
24678         else if(pn = el.up('td.x-date-mp-month', 2)){
24679             this.mpMonths.removeClass('x-date-mp-sel');
24680             pn.addClass('x-date-mp-sel');
24681             this.mpSelMonth = pn.dom.xmonth;
24682         }
24683         else if(pn = el.up('td.x-date-mp-year', 2)){
24684             this.mpYears.removeClass('x-date-mp-sel');
24685             pn.addClass('x-date-mp-sel');
24686             this.mpSelYear = pn.dom.xyear;
24687         }
24688         else if(el.is('a.x-date-mp-prev')){
24689             this.updateMPYear(this.mpyear-10);
24690         }
24691         else if(el.is('a.x-date-mp-next')){
24692             this.updateMPYear(this.mpyear+10);
24693         }
24694     },
24695
24696     onMonthDblClick : function(e, t){
24697         e.stopEvent();
24698         var el = new Roo.Element(t), pn;
24699         if(pn = el.up('td.x-date-mp-month', 2)){
24700             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24701             this.hideMonthPicker();
24702         }
24703         else if(pn = el.up('td.x-date-mp-year', 2)){
24704             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24705             this.hideMonthPicker();
24706         }
24707     },
24708
24709     hideMonthPicker : function(disableAnim){
24710         if(this.monthPicker){
24711             if(disableAnim === true){
24712                 this.monthPicker.hide();
24713             }else{
24714                 this.monthPicker.slideOut('t', {duration:.2});
24715             }
24716         }
24717     },
24718
24719     // private
24720     showPrevMonth : function(e){
24721         this.update(this.activeDate.add("mo", -1));
24722     },
24723
24724     // private
24725     showNextMonth : function(e){
24726         this.update(this.activeDate.add("mo", 1));
24727     },
24728
24729     // private
24730     showPrevYear : function(){
24731         this.update(this.activeDate.add("y", -1));
24732     },
24733
24734     // private
24735     showNextYear : function(){
24736         this.update(this.activeDate.add("y", 1));
24737     },
24738
24739     // private
24740     handleMouseWheel : function(e){
24741         var delta = e.getWheelDelta();
24742         if(delta > 0){
24743             this.showPrevMonth();
24744             e.stopEvent();
24745         } else if(delta < 0){
24746             this.showNextMonth();
24747             e.stopEvent();
24748         }
24749     },
24750
24751     // private
24752     handleDateClick : function(e, t){
24753         e.stopEvent();
24754         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24755             this.setValue(new Date(t.dateValue));
24756             this.fireEvent("select", this, this.value);
24757         }
24758     },
24759
24760     // private
24761     selectToday : function(){
24762         this.setValue(new Date().clearTime());
24763         this.fireEvent("select", this, this.value);
24764     },
24765
24766     // private
24767     update : function(date){
24768         var vd = this.activeDate;
24769         this.activeDate = date;
24770         if(vd && this.el){
24771             var t = date.getTime();
24772             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24773                 this.cells.removeClass("x-date-selected");
24774                 this.cells.each(function(c){
24775                    if(c.dom.firstChild.dateValue == t){
24776                        c.addClass("x-date-selected");
24777                        setTimeout(function(){
24778                             try{c.dom.firstChild.focus();}catch(e){}
24779                        }, 50);
24780                        return false;
24781                    }
24782                 });
24783                 return;
24784             }
24785         }
24786         var days = date.getDaysInMonth();
24787         var firstOfMonth = date.getFirstDateOfMonth();
24788         var startingPos = firstOfMonth.getDay()-this.startDay;
24789
24790         if(startingPos <= this.startDay){
24791             startingPos += 7;
24792         }
24793
24794         var pm = date.add("mo", -1);
24795         var prevStart = pm.getDaysInMonth()-startingPos;
24796
24797         var cells = this.cells.elements;
24798         var textEls = this.textNodes;
24799         days += startingPos;
24800
24801         // convert everything to numbers so it's fast
24802         var day = 86400000;
24803         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24804         var today = new Date().clearTime().getTime();
24805         var sel = date.clearTime().getTime();
24806         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24807         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24808         var ddMatch = this.disabledDatesRE;
24809         var ddText = this.disabledDatesText;
24810         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24811         var ddaysText = this.disabledDaysText;
24812         var format = this.format;
24813
24814         var setCellClass = function(cal, cell){
24815             cell.title = "";
24816             var t = d.getTime();
24817             cell.firstChild.dateValue = t;
24818             if(t == today){
24819                 cell.className += " x-date-today";
24820                 cell.title = cal.todayText;
24821             }
24822             if(t == sel){
24823                 cell.className += " x-date-selected";
24824                 setTimeout(function(){
24825                     try{cell.firstChild.focus();}catch(e){}
24826                 }, 50);
24827             }
24828             // disabling
24829             if(t < min) {
24830                 cell.className = " x-date-disabled";
24831                 cell.title = cal.minText;
24832                 return;
24833             }
24834             if(t > max) {
24835                 cell.className = " x-date-disabled";
24836                 cell.title = cal.maxText;
24837                 return;
24838             }
24839             if(ddays){
24840                 if(ddays.indexOf(d.getDay()) != -1){
24841                     cell.title = ddaysText;
24842                     cell.className = " x-date-disabled";
24843                 }
24844             }
24845             if(ddMatch && format){
24846                 var fvalue = d.dateFormat(format);
24847                 if(ddMatch.test(fvalue)){
24848                     cell.title = ddText.replace("%0", fvalue);
24849                     cell.className = " x-date-disabled";
24850                 }
24851             }
24852         };
24853
24854         var i = 0;
24855         for(; i < startingPos; i++) {
24856             textEls[i].innerHTML = (++prevStart);
24857             d.setDate(d.getDate()+1);
24858             cells[i].className = "x-date-prevday";
24859             setCellClass(this, cells[i]);
24860         }
24861         for(; i < days; i++){
24862             intDay = i - startingPos + 1;
24863             textEls[i].innerHTML = (intDay);
24864             d.setDate(d.getDate()+1);
24865             cells[i].className = "x-date-active";
24866             setCellClass(this, cells[i]);
24867         }
24868         var extraDays = 0;
24869         for(; i < 42; i++) {
24870              textEls[i].innerHTML = (++extraDays);
24871              d.setDate(d.getDate()+1);
24872              cells[i].className = "x-date-nextday";
24873              setCellClass(this, cells[i]);
24874         }
24875
24876         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24877
24878         if(!this.internalRender){
24879             var main = this.el.dom.firstChild;
24880             var w = main.offsetWidth;
24881             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24882             Roo.fly(main).setWidth(w);
24883             this.internalRender = true;
24884             // opera does not respect the auto grow header center column
24885             // then, after it gets a width opera refuses to recalculate
24886             // without a second pass
24887             if(Roo.isOpera && !this.secondPass){
24888                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24889                 this.secondPass = true;
24890                 this.update.defer(10, this, [date]);
24891             }
24892         }
24893     }
24894 });/*
24895  * Based on:
24896  * Ext JS Library 1.1.1
24897  * Copyright(c) 2006-2007, Ext JS, LLC.
24898  *
24899  * Originally Released Under LGPL - original licence link has changed is not relivant.
24900  *
24901  * Fork - LGPL
24902  * <script type="text/javascript">
24903  */
24904 /**
24905  * @class Roo.TabPanel
24906  * @extends Roo.util.Observable
24907  * A lightweight tab container.
24908  * <br><br>
24909  * Usage:
24910  * <pre><code>
24911 // basic tabs 1, built from existing content
24912 var tabs = new Roo.TabPanel("tabs1");
24913 tabs.addTab("script", "View Script");
24914 tabs.addTab("markup", "View Markup");
24915 tabs.activate("script");
24916
24917 // more advanced tabs, built from javascript
24918 var jtabs = new Roo.TabPanel("jtabs");
24919 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24920
24921 // set up the UpdateManager
24922 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24923 var updater = tab2.getUpdateManager();
24924 updater.setDefaultUrl("ajax1.htm");
24925 tab2.on('activate', updater.refresh, updater, true);
24926
24927 // Use setUrl for Ajax loading
24928 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24929 tab3.setUrl("ajax2.htm", null, true);
24930
24931 // Disabled tab
24932 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24933 tab4.disable();
24934
24935 jtabs.activate("jtabs-1");
24936  * </code></pre>
24937  * @constructor
24938  * Create a new TabPanel.
24939  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24940  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24941  */
24942 Roo.TabPanel = function(container, config){
24943     /**
24944     * The container element for this TabPanel.
24945     * @type Roo.Element
24946     */
24947     this.el = Roo.get(container, true);
24948     if(config){
24949         if(typeof config == "boolean"){
24950             this.tabPosition = config ? "bottom" : "top";
24951         }else{
24952             Roo.apply(this, config);
24953         }
24954     }
24955     if(this.tabPosition == "bottom"){
24956         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24957         this.el.addClass("x-tabs-bottom");
24958     }
24959     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24960     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24961     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24962     if(Roo.isIE){
24963         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24964     }
24965     if(this.tabPosition != "bottom"){
24966     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24967      * @type Roo.Element
24968      */
24969       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24970       this.el.addClass("x-tabs-top");
24971     }
24972     this.items = [];
24973
24974     this.bodyEl.setStyle("position", "relative");
24975
24976     this.active = null;
24977     this.activateDelegate = this.activate.createDelegate(this);
24978
24979     this.addEvents({
24980         /**
24981          * @event tabchange
24982          * Fires when the active tab changes
24983          * @param {Roo.TabPanel} this
24984          * @param {Roo.TabPanelItem} activePanel The new active tab
24985          */
24986         "tabchange": true,
24987         /**
24988          * @event beforetabchange
24989          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24990          * @param {Roo.TabPanel} this
24991          * @param {Object} e Set cancel to true on this object to cancel the tab change
24992          * @param {Roo.TabPanelItem} tab The tab being changed to
24993          */
24994         "beforetabchange" : true
24995     });
24996
24997     Roo.EventManager.onWindowResize(this.onResize, this);
24998     this.cpad = this.el.getPadding("lr");
24999     this.hiddenCount = 0;
25000
25001     Roo.TabPanel.superclass.constructor.call(this);
25002 };
25003
25004 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25005         /*
25006          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25007          */
25008     tabPosition : "top",
25009         /*
25010          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25011          */
25012     currentTabWidth : 0,
25013         /*
25014          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25015          */
25016     minTabWidth : 40,
25017         /*
25018          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25019          */
25020     maxTabWidth : 250,
25021         /*
25022          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25023          */
25024     preferredTabWidth : 175,
25025         /*
25026          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25027          */
25028     resizeTabs : false,
25029         /*
25030          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25031          */
25032     monitorResize : true,
25033
25034     /**
25035      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25036      * @param {String} id The id of the div to use <b>or create</b>
25037      * @param {String} text The text for the tab
25038      * @param {String} content (optional) Content to put in the TabPanelItem body
25039      * @param {Boolean} closable (optional) True to create a close icon on the tab
25040      * @return {Roo.TabPanelItem} The created TabPanelItem
25041      */
25042     addTab : function(id, text, content, closable){
25043         var item = new Roo.TabPanelItem(this, id, text, closable);
25044         this.addTabItem(item);
25045         if(content){
25046             item.setContent(content);
25047         }
25048         return item;
25049     },
25050
25051     /**
25052      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25053      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25054      * @return {Roo.TabPanelItem}
25055      */
25056     getTab : function(id){
25057         return this.items[id];
25058     },
25059
25060     /**
25061      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25062      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25063      */
25064     hideTab : function(id){
25065         var t = this.items[id];
25066         if(!t.isHidden()){
25067            t.setHidden(true);
25068            this.hiddenCount++;
25069            this.autoSizeTabs();
25070         }
25071     },
25072
25073     /**
25074      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25075      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25076      */
25077     unhideTab : function(id){
25078         var t = this.items[id];
25079         if(t.isHidden()){
25080            t.setHidden(false);
25081            this.hiddenCount--;
25082            this.autoSizeTabs();
25083         }
25084     },
25085
25086     /**
25087      * Adds an existing {@link Roo.TabPanelItem}.
25088      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25089      */
25090     addTabItem : function(item){
25091         this.items[item.id] = item;
25092         this.items.push(item);
25093         if(this.resizeTabs){
25094            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25095            this.autoSizeTabs();
25096         }else{
25097             item.autoSize();
25098         }
25099     },
25100
25101     /**
25102      * Removes a {@link Roo.TabPanelItem}.
25103      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25104      */
25105     removeTab : function(id){
25106         var items = this.items;
25107         var tab = items[id];
25108         if(!tab) { return; }
25109         var index = items.indexOf(tab);
25110         if(this.active == tab && items.length > 1){
25111             var newTab = this.getNextAvailable(index);
25112             if(newTab) {
25113                 newTab.activate();
25114             }
25115         }
25116         this.stripEl.dom.removeChild(tab.pnode.dom);
25117         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25118             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25119         }
25120         items.splice(index, 1);
25121         delete this.items[tab.id];
25122         tab.fireEvent("close", tab);
25123         tab.purgeListeners();
25124         this.autoSizeTabs();
25125     },
25126
25127     getNextAvailable : function(start){
25128         var items = this.items;
25129         var index = start;
25130         // look for a next tab that will slide over to
25131         // replace the one being removed
25132         while(index < items.length){
25133             var item = items[++index];
25134             if(item && !item.isHidden()){
25135                 return item;
25136             }
25137         }
25138         // if one isn't found select the previous tab (on the left)
25139         index = start;
25140         while(index >= 0){
25141             var item = items[--index];
25142             if(item && !item.isHidden()){
25143                 return item;
25144             }
25145         }
25146         return null;
25147     },
25148
25149     /**
25150      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25151      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25152      */
25153     disableTab : function(id){
25154         var tab = this.items[id];
25155         if(tab && this.active != tab){
25156             tab.disable();
25157         }
25158     },
25159
25160     /**
25161      * Enables a {@link Roo.TabPanelItem} that is disabled.
25162      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25163      */
25164     enableTab : function(id){
25165         var tab = this.items[id];
25166         tab.enable();
25167     },
25168
25169     /**
25170      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25171      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25172      * @return {Roo.TabPanelItem} The TabPanelItem.
25173      */
25174     activate : function(id){
25175         var tab = this.items[id];
25176         if(!tab){
25177             return null;
25178         }
25179         if(tab == this.active || tab.disabled){
25180             return tab;
25181         }
25182         var e = {};
25183         this.fireEvent("beforetabchange", this, e, tab);
25184         if(e.cancel !== true && !tab.disabled){
25185             if(this.active){
25186                 this.active.hide();
25187             }
25188             this.active = this.items[id];
25189             this.active.show();
25190             this.fireEvent("tabchange", this, this.active);
25191         }
25192         return tab;
25193     },
25194
25195     /**
25196      * Gets the active {@link Roo.TabPanelItem}.
25197      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25198      */
25199     getActiveTab : function(){
25200         return this.active;
25201     },
25202
25203     /**
25204      * Updates the tab body element to fit the height of the container element
25205      * for overflow scrolling
25206      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25207      */
25208     syncHeight : function(targetHeight){
25209         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25210         var bm = this.bodyEl.getMargins();
25211         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25212         this.bodyEl.setHeight(newHeight);
25213         return newHeight;
25214     },
25215
25216     onResize : function(){
25217         if(this.monitorResize){
25218             this.autoSizeTabs();
25219         }
25220     },
25221
25222     /**
25223      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25224      */
25225     beginUpdate : function(){
25226         this.updating = true;
25227     },
25228
25229     /**
25230      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25231      */
25232     endUpdate : function(){
25233         this.updating = false;
25234         this.autoSizeTabs();
25235     },
25236
25237     /**
25238      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25239      */
25240     autoSizeTabs : function(){
25241         var count = this.items.length;
25242         var vcount = count - this.hiddenCount;
25243         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25244         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25245         var availWidth = Math.floor(w / vcount);
25246         var b = this.stripBody;
25247         if(b.getWidth() > w){
25248             var tabs = this.items;
25249             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25250             if(availWidth < this.minTabWidth){
25251                 /*if(!this.sleft){    // incomplete scrolling code
25252                     this.createScrollButtons();
25253                 }
25254                 this.showScroll();
25255                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25256             }
25257         }else{
25258             if(this.currentTabWidth < this.preferredTabWidth){
25259                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25260             }
25261         }
25262     },
25263
25264     /**
25265      * Returns the number of tabs in this TabPanel.
25266      * @return {Number}
25267      */
25268      getCount : function(){
25269          return this.items.length;
25270      },
25271
25272     /**
25273      * Resizes all the tabs to the passed width
25274      * @param {Number} The new width
25275      */
25276     setTabWidth : function(width){
25277         this.currentTabWidth = width;
25278         for(var i = 0, len = this.items.length; i < len; i++) {
25279                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25280         }
25281     },
25282
25283     /**
25284      * Destroys this TabPanel
25285      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25286      */
25287     destroy : function(removeEl){
25288         Roo.EventManager.removeResizeListener(this.onResize, this);
25289         for(var i = 0, len = this.items.length; i < len; i++){
25290             this.items[i].purgeListeners();
25291         }
25292         if(removeEl === true){
25293             this.el.update("");
25294             this.el.remove();
25295         }
25296     }
25297 });
25298
25299 /**
25300  * @class Roo.TabPanelItem
25301  * @extends Roo.util.Observable
25302  * Represents an individual item (tab plus body) in a TabPanel.
25303  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25304  * @param {String} id The id of this TabPanelItem
25305  * @param {String} text The text for the tab of this TabPanelItem
25306  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25307  */
25308 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25309     /**
25310      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25311      * @type Roo.TabPanel
25312      */
25313     this.tabPanel = tabPanel;
25314     /**
25315      * The id for this TabPanelItem
25316      * @type String
25317      */
25318     this.id = id;
25319     /** @private */
25320     this.disabled = false;
25321     /** @private */
25322     this.text = text;
25323     /** @private */
25324     this.loaded = false;
25325     this.closable = closable;
25326
25327     /**
25328      * The body element for this TabPanelItem.
25329      * @type Roo.Element
25330      */
25331     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25332     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25333     this.bodyEl.setStyle("display", "block");
25334     this.bodyEl.setStyle("zoom", "1");
25335     this.hideAction();
25336
25337     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25338     /** @private */
25339     this.el = Roo.get(els.el, true);
25340     this.inner = Roo.get(els.inner, true);
25341     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25342     this.pnode = Roo.get(els.el.parentNode, true);
25343     this.el.on("mousedown", this.onTabMouseDown, this);
25344     this.el.on("click", this.onTabClick, this);
25345     /** @private */
25346     if(closable){
25347         var c = Roo.get(els.close, true);
25348         c.dom.title = this.closeText;
25349         c.addClassOnOver("close-over");
25350         c.on("click", this.closeClick, this);
25351      }
25352
25353     this.addEvents({
25354          /**
25355          * @event activate
25356          * Fires when this tab becomes the active tab.
25357          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25358          * @param {Roo.TabPanelItem} this
25359          */
25360         "activate": true,
25361         /**
25362          * @event beforeclose
25363          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25364          * @param {Roo.TabPanelItem} this
25365          * @param {Object} e Set cancel to true on this object to cancel the close.
25366          */
25367         "beforeclose": true,
25368         /**
25369          * @event close
25370          * Fires when this tab is closed.
25371          * @param {Roo.TabPanelItem} this
25372          */
25373          "close": true,
25374         /**
25375          * @event deactivate
25376          * Fires when this tab is no longer the active tab.
25377          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25378          * @param {Roo.TabPanelItem} this
25379          */
25380          "deactivate" : true
25381     });
25382     this.hidden = false;
25383
25384     Roo.TabPanelItem.superclass.constructor.call(this);
25385 };
25386
25387 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25388     purgeListeners : function(){
25389        Roo.util.Observable.prototype.purgeListeners.call(this);
25390        this.el.removeAllListeners();
25391     },
25392     /**
25393      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25394      */
25395     show : function(){
25396         this.pnode.addClass("on");
25397         this.showAction();
25398         if(Roo.isOpera){
25399             this.tabPanel.stripWrap.repaint();
25400         }
25401         this.fireEvent("activate", this.tabPanel, this);
25402     },
25403
25404     /**
25405      * Returns true if this tab is the active tab.
25406      * @return {Boolean}
25407      */
25408     isActive : function(){
25409         return this.tabPanel.getActiveTab() == this;
25410     },
25411
25412     /**
25413      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25414      */
25415     hide : function(){
25416         this.pnode.removeClass("on");
25417         this.hideAction();
25418         this.fireEvent("deactivate", this.tabPanel, this);
25419     },
25420
25421     hideAction : function(){
25422         this.bodyEl.hide();
25423         this.bodyEl.setStyle("position", "absolute");
25424         this.bodyEl.setLeft("-20000px");
25425         this.bodyEl.setTop("-20000px");
25426     },
25427
25428     showAction : function(){
25429         this.bodyEl.setStyle("position", "relative");
25430         this.bodyEl.setTop("");
25431         this.bodyEl.setLeft("");
25432         this.bodyEl.show();
25433     },
25434
25435     /**
25436      * Set the tooltip for the tab.
25437      * @param {String} tooltip The tab's tooltip
25438      */
25439     setTooltip : function(text){
25440         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25441             this.textEl.dom.qtip = text;
25442             this.textEl.dom.removeAttribute('title');
25443         }else{
25444             this.textEl.dom.title = text;
25445         }
25446     },
25447
25448     onTabClick : function(e){
25449         e.preventDefault();
25450         this.tabPanel.activate(this.id);
25451     },
25452
25453     onTabMouseDown : function(e){
25454         e.preventDefault();
25455         this.tabPanel.activate(this.id);
25456     },
25457
25458     getWidth : function(){
25459         return this.inner.getWidth();
25460     },
25461
25462     setWidth : function(width){
25463         var iwidth = width - this.pnode.getPadding("lr");
25464         this.inner.setWidth(iwidth);
25465         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25466         this.pnode.setWidth(width);
25467     },
25468
25469     /**
25470      * Show or hide the tab
25471      * @param {Boolean} hidden True to hide or false to show.
25472      */
25473     setHidden : function(hidden){
25474         this.hidden = hidden;
25475         this.pnode.setStyle("display", hidden ? "none" : "");
25476     },
25477
25478     /**
25479      * Returns true if this tab is "hidden"
25480      * @return {Boolean}
25481      */
25482     isHidden : function(){
25483         return this.hidden;
25484     },
25485
25486     /**
25487      * Returns the text for this tab
25488      * @return {String}
25489      */
25490     getText : function(){
25491         return this.text;
25492     },
25493
25494     autoSize : function(){
25495         //this.el.beginMeasure();
25496         this.textEl.setWidth(1);
25497         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25498         //this.el.endMeasure();
25499     },
25500
25501     /**
25502      * Sets the text for the tab (Note: this also sets the tooltip text)
25503      * @param {String} text The tab's text and tooltip
25504      */
25505     setText : function(text){
25506         this.text = text;
25507         this.textEl.update(text);
25508         this.setTooltip(text);
25509         if(!this.tabPanel.resizeTabs){
25510             this.autoSize();
25511         }
25512     },
25513     /**
25514      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25515      */
25516     activate : function(){
25517         this.tabPanel.activate(this.id);
25518     },
25519
25520     /**
25521      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25522      */
25523     disable : function(){
25524         if(this.tabPanel.active != this){
25525             this.disabled = true;
25526             this.pnode.addClass("disabled");
25527         }
25528     },
25529
25530     /**
25531      * Enables this TabPanelItem if it was previously disabled.
25532      */
25533     enable : function(){
25534         this.disabled = false;
25535         this.pnode.removeClass("disabled");
25536     },
25537
25538     /**
25539      * Sets the content for this TabPanelItem.
25540      * @param {String} content The content
25541      * @param {Boolean} loadScripts true to look for and load scripts
25542      */
25543     setContent : function(content, loadScripts){
25544         this.bodyEl.update(content, loadScripts);
25545     },
25546
25547     /**
25548      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25549      * @return {Roo.UpdateManager} The UpdateManager
25550      */
25551     getUpdateManager : function(){
25552         return this.bodyEl.getUpdateManager();
25553     },
25554
25555     /**
25556      * Set a URL to be used to load the content for this TabPanelItem.
25557      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25558      * @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)
25559      * @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)
25560      * @return {Roo.UpdateManager} The UpdateManager
25561      */
25562     setUrl : function(url, params, loadOnce){
25563         if(this.refreshDelegate){
25564             this.un('activate', this.refreshDelegate);
25565         }
25566         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25567         this.on("activate", this.refreshDelegate);
25568         return this.bodyEl.getUpdateManager();
25569     },
25570
25571     /** @private */
25572     _handleRefresh : function(url, params, loadOnce){
25573         if(!loadOnce || !this.loaded){
25574             var updater = this.bodyEl.getUpdateManager();
25575             updater.update(url, params, this._setLoaded.createDelegate(this));
25576         }
25577     },
25578
25579     /**
25580      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25581      *   Will fail silently if the setUrl method has not been called.
25582      *   This does not activate the panel, just updates its content.
25583      */
25584     refresh : function(){
25585         if(this.refreshDelegate){
25586            this.loaded = false;
25587            this.refreshDelegate();
25588         }
25589     },
25590
25591     /** @private */
25592     _setLoaded : function(){
25593         this.loaded = true;
25594     },
25595
25596     /** @private */
25597     closeClick : function(e){
25598         var o = {};
25599         e.stopEvent();
25600         this.fireEvent("beforeclose", this, o);
25601         if(o.cancel !== true){
25602             this.tabPanel.removeTab(this.id);
25603         }
25604     },
25605     /**
25606      * The text displayed in the tooltip for the close icon.
25607      * @type String
25608      */
25609     closeText : "Close this tab"
25610 });
25611
25612 /** @private */
25613 Roo.TabPanel.prototype.createStrip = function(container){
25614     var strip = document.createElement("div");
25615     strip.className = "x-tabs-wrap";
25616     container.appendChild(strip);
25617     return strip;
25618 };
25619 /** @private */
25620 Roo.TabPanel.prototype.createStripList = function(strip){
25621     // div wrapper for retard IE
25622     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>';
25623     return strip.firstChild.firstChild.firstChild.firstChild;
25624 };
25625 /** @private */
25626 Roo.TabPanel.prototype.createBody = function(container){
25627     var body = document.createElement("div");
25628     Roo.id(body, "tab-body");
25629     Roo.fly(body).addClass("x-tabs-body");
25630     container.appendChild(body);
25631     return body;
25632 };
25633 /** @private */
25634 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25635     var body = Roo.getDom(id);
25636     if(!body){
25637         body = document.createElement("div");
25638         body.id = id;
25639     }
25640     Roo.fly(body).addClass("x-tabs-item-body");
25641     bodyEl.insertBefore(body, bodyEl.firstChild);
25642     return body;
25643 };
25644 /** @private */
25645 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25646     var td = document.createElement("td");
25647     stripEl.appendChild(td);
25648     if(closable){
25649         td.className = "x-tabs-closable";
25650         if(!this.closeTpl){
25651             this.closeTpl = new Roo.Template(
25652                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25653                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25654                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25655             );
25656         }
25657         var el = this.closeTpl.overwrite(td, {"text": text});
25658         var close = el.getElementsByTagName("div")[0];
25659         var inner = el.getElementsByTagName("em")[0];
25660         return {"el": el, "close": close, "inner": inner};
25661     } else {
25662         if(!this.tabTpl){
25663             this.tabTpl = new Roo.Template(
25664                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25665                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25666             );
25667         }
25668         var el = this.tabTpl.overwrite(td, {"text": text});
25669         var inner = el.getElementsByTagName("em")[0];
25670         return {"el": el, "inner": inner};
25671     }
25672 };/*
25673  * Based on:
25674  * Ext JS Library 1.1.1
25675  * Copyright(c) 2006-2007, Ext JS, LLC.
25676  *
25677  * Originally Released Under LGPL - original licence link has changed is not relivant.
25678  *
25679  * Fork - LGPL
25680  * <script type="text/javascript">
25681  */
25682
25683 /**
25684  * @class Roo.Button
25685  * @extends Roo.util.Observable
25686  * Simple Button class
25687  * @cfg {String} text The button text
25688  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25689  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25690  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25691  * @cfg {Object} scope The scope of the handler
25692  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25693  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25694  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25695  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25696  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25697  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25698    applies if enableToggle = true)
25699  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25700  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25701   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25702  * @constructor
25703  * Create a new button
25704  * @param {Object} config The config object
25705  */
25706 Roo.Button = function(renderTo, config)
25707 {
25708     if (!config) {
25709         config = renderTo;
25710         renderTo = config.renderTo || false;
25711     }
25712     
25713     Roo.apply(this, config);
25714     this.addEvents({
25715         /**
25716              * @event click
25717              * Fires when this button is clicked
25718              * @param {Button} this
25719              * @param {EventObject} e The click event
25720              */
25721             "click" : true,
25722         /**
25723              * @event toggle
25724              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25725              * @param {Button} this
25726              * @param {Boolean} pressed
25727              */
25728             "toggle" : true,
25729         /**
25730              * @event mouseover
25731              * Fires when the mouse hovers over the button
25732              * @param {Button} this
25733              * @param {Event} e The event object
25734              */
25735         'mouseover' : true,
25736         /**
25737              * @event mouseout
25738              * Fires when the mouse exits the button
25739              * @param {Button} this
25740              * @param {Event} e The event object
25741              */
25742         'mouseout': true,
25743          /**
25744              * @event render
25745              * Fires when the button is rendered
25746              * @param {Button} this
25747              */
25748         'render': true
25749     });
25750     if(this.menu){
25751         this.menu = Roo.menu.MenuMgr.get(this.menu);
25752     }
25753     if(renderTo){
25754         this.render(renderTo);
25755     }
25756     
25757     Roo.util.Observable.call(this);
25758 };
25759
25760 Roo.extend(Roo.Button, Roo.util.Observable, {
25761     /**
25762      * 
25763      */
25764     
25765     /**
25766      * Read-only. True if this button is hidden
25767      * @type Boolean
25768      */
25769     hidden : false,
25770     /**
25771      * Read-only. True if this button is disabled
25772      * @type Boolean
25773      */
25774     disabled : false,
25775     /**
25776      * Read-only. True if this button is pressed (only if enableToggle = true)
25777      * @type Boolean
25778      */
25779     pressed : false,
25780
25781     /**
25782      * @cfg {Number} tabIndex 
25783      * The DOM tabIndex for this button (defaults to undefined)
25784      */
25785     tabIndex : undefined,
25786
25787     /**
25788      * @cfg {Boolean} enableToggle
25789      * True to enable pressed/not pressed toggling (defaults to false)
25790      */
25791     enableToggle: false,
25792     /**
25793      * @cfg {Mixed} menu
25794      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25795      */
25796     menu : undefined,
25797     /**
25798      * @cfg {String} menuAlign
25799      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25800      */
25801     menuAlign : "tl-bl?",
25802
25803     /**
25804      * @cfg {String} iconCls
25805      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25806      */
25807     iconCls : undefined,
25808     /**
25809      * @cfg {String} type
25810      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25811      */
25812     type : 'button',
25813
25814     // private
25815     menuClassTarget: 'tr',
25816
25817     /**
25818      * @cfg {String} clickEvent
25819      * The type of event to map to the button's event handler (defaults to 'click')
25820      */
25821     clickEvent : 'click',
25822
25823     /**
25824      * @cfg {Boolean} handleMouseEvents
25825      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25826      */
25827     handleMouseEvents : true,
25828
25829     /**
25830      * @cfg {String} tooltipType
25831      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25832      */
25833     tooltipType : 'qtip',
25834
25835     /**
25836      * @cfg {String} cls
25837      * A CSS class to apply to the button's main element.
25838      */
25839     
25840     /**
25841      * @cfg {Roo.Template} template (Optional)
25842      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25843      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25844      * require code modifications if required elements (e.g. a button) aren't present.
25845      */
25846
25847     // private
25848     render : function(renderTo){
25849         var btn;
25850         if(this.hideParent){
25851             this.parentEl = Roo.get(renderTo);
25852         }
25853         if(!this.dhconfig){
25854             if(!this.template){
25855                 if(!Roo.Button.buttonTemplate){
25856                     // hideous table template
25857                     Roo.Button.buttonTemplate = new Roo.Template(
25858                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25859                         '<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>',
25860                         "</tr></tbody></table>");
25861                 }
25862                 this.template = Roo.Button.buttonTemplate;
25863             }
25864             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25865             var btnEl = btn.child("button:first");
25866             btnEl.on('focus', this.onFocus, this);
25867             btnEl.on('blur', this.onBlur, this);
25868             if(this.cls){
25869                 btn.addClass(this.cls);
25870             }
25871             if(this.icon){
25872                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25873             }
25874             if(this.iconCls){
25875                 btnEl.addClass(this.iconCls);
25876                 if(!this.cls){
25877                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25878                 }
25879             }
25880             if(this.tabIndex !== undefined){
25881                 btnEl.dom.tabIndex = this.tabIndex;
25882             }
25883             if(this.tooltip){
25884                 if(typeof this.tooltip == 'object'){
25885                     Roo.QuickTips.tips(Roo.apply({
25886                           target: btnEl.id
25887                     }, this.tooltip));
25888                 } else {
25889                     btnEl.dom[this.tooltipType] = this.tooltip;
25890                 }
25891             }
25892         }else{
25893             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25894         }
25895         this.el = btn;
25896         if(this.id){
25897             this.el.dom.id = this.el.id = this.id;
25898         }
25899         if(this.menu){
25900             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25901             this.menu.on("show", this.onMenuShow, this);
25902             this.menu.on("hide", this.onMenuHide, this);
25903         }
25904         btn.addClass("x-btn");
25905         if(Roo.isIE && !Roo.isIE7){
25906             this.autoWidth.defer(1, this);
25907         }else{
25908             this.autoWidth();
25909         }
25910         if(this.handleMouseEvents){
25911             btn.on("mouseover", this.onMouseOver, this);
25912             btn.on("mouseout", this.onMouseOut, this);
25913             btn.on("mousedown", this.onMouseDown, this);
25914         }
25915         btn.on(this.clickEvent, this.onClick, this);
25916         //btn.on("mouseup", this.onMouseUp, this);
25917         if(this.hidden){
25918             this.hide();
25919         }
25920         if(this.disabled){
25921             this.disable();
25922         }
25923         Roo.ButtonToggleMgr.register(this);
25924         if(this.pressed){
25925             this.el.addClass("x-btn-pressed");
25926         }
25927         if(this.repeat){
25928             var repeater = new Roo.util.ClickRepeater(btn,
25929                 typeof this.repeat == "object" ? this.repeat : {}
25930             );
25931             repeater.on("click", this.onClick,  this);
25932         }
25933         this.fireEvent('render', this);
25934         
25935     },
25936     /**
25937      * Returns the button's underlying element
25938      * @return {Roo.Element} The element
25939      */
25940     getEl : function(){
25941         return this.el;  
25942     },
25943     
25944     /**
25945      * Destroys this Button and removes any listeners.
25946      */
25947     destroy : function(){
25948         Roo.ButtonToggleMgr.unregister(this);
25949         this.el.removeAllListeners();
25950         this.purgeListeners();
25951         this.el.remove();
25952     },
25953
25954     // private
25955     autoWidth : function(){
25956         if(this.el){
25957             this.el.setWidth("auto");
25958             if(Roo.isIE7 && Roo.isStrict){
25959                 var ib = this.el.child('button');
25960                 if(ib && ib.getWidth() > 20){
25961                     ib.clip();
25962                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25963                 }
25964             }
25965             if(this.minWidth){
25966                 if(this.hidden){
25967                     this.el.beginMeasure();
25968                 }
25969                 if(this.el.getWidth() < this.minWidth){
25970                     this.el.setWidth(this.minWidth);
25971                 }
25972                 if(this.hidden){
25973                     this.el.endMeasure();
25974                 }
25975             }
25976         }
25977     },
25978
25979     /**
25980      * Assigns this button's click handler
25981      * @param {Function} handler The function to call when the button is clicked
25982      * @param {Object} scope (optional) Scope for the function passed in
25983      */
25984     setHandler : function(handler, scope){
25985         this.handler = handler;
25986         this.scope = scope;  
25987     },
25988     
25989     /**
25990      * Sets this button's text
25991      * @param {String} text The button text
25992      */
25993     setText : function(text){
25994         this.text = text;
25995         if(this.el){
25996             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25997         }
25998         this.autoWidth();
25999     },
26000     
26001     /**
26002      * Gets the text for this button
26003      * @return {String} The button text
26004      */
26005     getText : function(){
26006         return this.text;  
26007     },
26008     
26009     /**
26010      * Show this button
26011      */
26012     show: function(){
26013         this.hidden = false;
26014         if(this.el){
26015             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26016         }
26017     },
26018     
26019     /**
26020      * Hide this button
26021      */
26022     hide: function(){
26023         this.hidden = true;
26024         if(this.el){
26025             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26026         }
26027     },
26028     
26029     /**
26030      * Convenience function for boolean show/hide
26031      * @param {Boolean} visible True to show, false to hide
26032      */
26033     setVisible: function(visible){
26034         if(visible) {
26035             this.show();
26036         }else{
26037             this.hide();
26038         }
26039     },
26040     
26041     /**
26042      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26043      * @param {Boolean} state (optional) Force a particular state
26044      */
26045     toggle : function(state){
26046         state = state === undefined ? !this.pressed : state;
26047         if(state != this.pressed){
26048             if(state){
26049                 this.el.addClass("x-btn-pressed");
26050                 this.pressed = true;
26051                 this.fireEvent("toggle", this, true);
26052             }else{
26053                 this.el.removeClass("x-btn-pressed");
26054                 this.pressed = false;
26055                 this.fireEvent("toggle", this, false);
26056             }
26057             if(this.toggleHandler){
26058                 this.toggleHandler.call(this.scope || this, this, state);
26059             }
26060         }
26061     },
26062     
26063     /**
26064      * Focus the button
26065      */
26066     focus : function(){
26067         this.el.child('button:first').focus();
26068     },
26069     
26070     /**
26071      * Disable this button
26072      */
26073     disable : function(){
26074         if(this.el){
26075             this.el.addClass("x-btn-disabled");
26076         }
26077         this.disabled = true;
26078     },
26079     
26080     /**
26081      * Enable this button
26082      */
26083     enable : function(){
26084         if(this.el){
26085             this.el.removeClass("x-btn-disabled");
26086         }
26087         this.disabled = false;
26088     },
26089
26090     /**
26091      * Convenience function for boolean enable/disable
26092      * @param {Boolean} enabled True to enable, false to disable
26093      */
26094     setDisabled : function(v){
26095         this[v !== true ? "enable" : "disable"]();
26096     },
26097
26098     // private
26099     onClick : function(e){
26100         if(e){
26101             e.preventDefault();
26102         }
26103         if(e.button != 0){
26104             return;
26105         }
26106         if(!this.disabled){
26107             if(this.enableToggle){
26108                 this.toggle();
26109             }
26110             if(this.menu && !this.menu.isVisible()){
26111                 this.menu.show(this.el, this.menuAlign);
26112             }
26113             this.fireEvent("click", this, e);
26114             if(this.handler){
26115                 this.el.removeClass("x-btn-over");
26116                 this.handler.call(this.scope || this, this, e);
26117             }
26118         }
26119     },
26120     // private
26121     onMouseOver : function(e){
26122         if(!this.disabled){
26123             this.el.addClass("x-btn-over");
26124             this.fireEvent('mouseover', this, e);
26125         }
26126     },
26127     // private
26128     onMouseOut : function(e){
26129         if(!e.within(this.el,  true)){
26130             this.el.removeClass("x-btn-over");
26131             this.fireEvent('mouseout', this, e);
26132         }
26133     },
26134     // private
26135     onFocus : function(e){
26136         if(!this.disabled){
26137             this.el.addClass("x-btn-focus");
26138         }
26139     },
26140     // private
26141     onBlur : function(e){
26142         this.el.removeClass("x-btn-focus");
26143     },
26144     // private
26145     onMouseDown : function(e){
26146         if(!this.disabled && e.button == 0){
26147             this.el.addClass("x-btn-click");
26148             Roo.get(document).on('mouseup', this.onMouseUp, this);
26149         }
26150     },
26151     // private
26152     onMouseUp : function(e){
26153         if(e.button == 0){
26154             this.el.removeClass("x-btn-click");
26155             Roo.get(document).un('mouseup', this.onMouseUp, this);
26156         }
26157     },
26158     // private
26159     onMenuShow : function(e){
26160         this.el.addClass("x-btn-menu-active");
26161     },
26162     // private
26163     onMenuHide : function(e){
26164         this.el.removeClass("x-btn-menu-active");
26165     }   
26166 });
26167
26168 // Private utility class used by Button
26169 Roo.ButtonToggleMgr = function(){
26170    var groups = {};
26171    
26172    function toggleGroup(btn, state){
26173        if(state){
26174            var g = groups[btn.toggleGroup];
26175            for(var i = 0, l = g.length; i < l; i++){
26176                if(g[i] != btn){
26177                    g[i].toggle(false);
26178                }
26179            }
26180        }
26181    }
26182    
26183    return {
26184        register : function(btn){
26185            if(!btn.toggleGroup){
26186                return;
26187            }
26188            var g = groups[btn.toggleGroup];
26189            if(!g){
26190                g = groups[btn.toggleGroup] = [];
26191            }
26192            g.push(btn);
26193            btn.on("toggle", toggleGroup);
26194        },
26195        
26196        unregister : function(btn){
26197            if(!btn.toggleGroup){
26198                return;
26199            }
26200            var g = groups[btn.toggleGroup];
26201            if(g){
26202                g.remove(btn);
26203                btn.un("toggle", toggleGroup);
26204            }
26205        }
26206    };
26207 }();/*
26208  * Based on:
26209  * Ext JS Library 1.1.1
26210  * Copyright(c) 2006-2007, Ext JS, LLC.
26211  *
26212  * Originally Released Under LGPL - original licence link has changed is not relivant.
26213  *
26214  * Fork - LGPL
26215  * <script type="text/javascript">
26216  */
26217  
26218 /**
26219  * @class Roo.SplitButton
26220  * @extends Roo.Button
26221  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26222  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26223  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26224  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26225  * @cfg {String} arrowTooltip The title attribute of the arrow
26226  * @constructor
26227  * Create a new menu button
26228  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26229  * @param {Object} config The config object
26230  */
26231 Roo.SplitButton = function(renderTo, config){
26232     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26233     /**
26234      * @event arrowclick
26235      * Fires when this button's arrow is clicked
26236      * @param {SplitButton} this
26237      * @param {EventObject} e The click event
26238      */
26239     this.addEvents({"arrowclick":true});
26240 };
26241
26242 Roo.extend(Roo.SplitButton, Roo.Button, {
26243     render : function(renderTo){
26244         // this is one sweet looking template!
26245         var tpl = new Roo.Template(
26246             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26247             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26248             '<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>',
26249             "</tbody></table></td><td>",
26250             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26251             '<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>',
26252             "</tbody></table></td></tr></table>"
26253         );
26254         var btn = tpl.append(renderTo, [this.text, this.type], true);
26255         var btnEl = btn.child("button");
26256         if(this.cls){
26257             btn.addClass(this.cls);
26258         }
26259         if(this.icon){
26260             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26261         }
26262         if(this.iconCls){
26263             btnEl.addClass(this.iconCls);
26264             if(!this.cls){
26265                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26266             }
26267         }
26268         this.el = btn;
26269         if(this.handleMouseEvents){
26270             btn.on("mouseover", this.onMouseOver, this);
26271             btn.on("mouseout", this.onMouseOut, this);
26272             btn.on("mousedown", this.onMouseDown, this);
26273             btn.on("mouseup", this.onMouseUp, this);
26274         }
26275         btn.on(this.clickEvent, this.onClick, this);
26276         if(this.tooltip){
26277             if(typeof this.tooltip == 'object'){
26278                 Roo.QuickTips.tips(Roo.apply({
26279                       target: btnEl.id
26280                 }, this.tooltip));
26281             } else {
26282                 btnEl.dom[this.tooltipType] = this.tooltip;
26283             }
26284         }
26285         if(this.arrowTooltip){
26286             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26287         }
26288         if(this.hidden){
26289             this.hide();
26290         }
26291         if(this.disabled){
26292             this.disable();
26293         }
26294         if(this.pressed){
26295             this.el.addClass("x-btn-pressed");
26296         }
26297         if(Roo.isIE && !Roo.isIE7){
26298             this.autoWidth.defer(1, this);
26299         }else{
26300             this.autoWidth();
26301         }
26302         if(this.menu){
26303             this.menu.on("show", this.onMenuShow, this);
26304             this.menu.on("hide", this.onMenuHide, this);
26305         }
26306         this.fireEvent('render', this);
26307     },
26308
26309     // private
26310     autoWidth : function(){
26311         if(this.el){
26312             var tbl = this.el.child("table:first");
26313             var tbl2 = this.el.child("table:last");
26314             this.el.setWidth("auto");
26315             tbl.setWidth("auto");
26316             if(Roo.isIE7 && Roo.isStrict){
26317                 var ib = this.el.child('button:first');
26318                 if(ib && ib.getWidth() > 20){
26319                     ib.clip();
26320                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26321                 }
26322             }
26323             if(this.minWidth){
26324                 if(this.hidden){
26325                     this.el.beginMeasure();
26326                 }
26327                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26328                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26329                 }
26330                 if(this.hidden){
26331                     this.el.endMeasure();
26332                 }
26333             }
26334             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26335         } 
26336     },
26337     /**
26338      * Sets this button's click handler
26339      * @param {Function} handler The function to call when the button is clicked
26340      * @param {Object} scope (optional) Scope for the function passed above
26341      */
26342     setHandler : function(handler, scope){
26343         this.handler = handler;
26344         this.scope = scope;  
26345     },
26346     
26347     /**
26348      * Sets this button's arrow click handler
26349      * @param {Function} handler The function to call when the arrow is clicked
26350      * @param {Object} scope (optional) Scope for the function passed above
26351      */
26352     setArrowHandler : function(handler, scope){
26353         this.arrowHandler = handler;
26354         this.scope = scope;  
26355     },
26356     
26357     /**
26358      * Focus the button
26359      */
26360     focus : function(){
26361         if(this.el){
26362             this.el.child("button:first").focus();
26363         }
26364     },
26365
26366     // private
26367     onClick : function(e){
26368         e.preventDefault();
26369         if(!this.disabled){
26370             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26371                 if(this.menu && !this.menu.isVisible()){
26372                     this.menu.show(this.el, this.menuAlign);
26373                 }
26374                 this.fireEvent("arrowclick", this, e);
26375                 if(this.arrowHandler){
26376                     this.arrowHandler.call(this.scope || this, this, e);
26377                 }
26378             }else{
26379                 this.fireEvent("click", this, e);
26380                 if(this.handler){
26381                     this.handler.call(this.scope || this, this, e);
26382                 }
26383             }
26384         }
26385     },
26386     // private
26387     onMouseDown : function(e){
26388         if(!this.disabled){
26389             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26390         }
26391     },
26392     // private
26393     onMouseUp : function(e){
26394         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26395     }   
26396 });
26397
26398
26399 // backwards compat
26400 Roo.MenuButton = Roo.SplitButton;/*
26401  * Based on:
26402  * Ext JS Library 1.1.1
26403  * Copyright(c) 2006-2007, Ext JS, LLC.
26404  *
26405  * Originally Released Under LGPL - original licence link has changed is not relivant.
26406  *
26407  * Fork - LGPL
26408  * <script type="text/javascript">
26409  */
26410
26411 /**
26412  * @class Roo.Toolbar
26413  * Basic Toolbar class.
26414  * @constructor
26415  * Creates a new Toolbar
26416  * @param {Object} config The config object
26417  */ 
26418 Roo.Toolbar = function(container, buttons, config)
26419 {
26420     /// old consturctor format still supported..
26421     if(container instanceof Array){ // omit the container for later rendering
26422         buttons = container;
26423         config = buttons;
26424         container = null;
26425     }
26426     if (typeof(container) == 'object' && container.xtype) {
26427         config = container;
26428         container = config.container;
26429         buttons = config.buttons; // not really - use items!!
26430     }
26431     var xitems = [];
26432     if (config && config.items) {
26433         xitems = config.items;
26434         delete config.items;
26435     }
26436     Roo.apply(this, config);
26437     this.buttons = buttons;
26438     
26439     if(container){
26440         this.render(container);
26441     }
26442     Roo.each(xitems, function(b) {
26443         this.add(b);
26444     }, this);
26445     
26446 };
26447
26448 Roo.Toolbar.prototype = {
26449     /**
26450      * @cfg {Roo.data.Store} items
26451      * array of button configs or elements to add
26452      */
26453     
26454     /**
26455      * @cfg {String/HTMLElement/Element} container
26456      * The id or element that will contain the toolbar
26457      */
26458     // private
26459     render : function(ct){
26460         this.el = Roo.get(ct);
26461         if(this.cls){
26462             this.el.addClass(this.cls);
26463         }
26464         // using a table allows for vertical alignment
26465         // 100% width is needed by Safari...
26466         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26467         this.tr = this.el.child("tr", true);
26468         var autoId = 0;
26469         this.items = new Roo.util.MixedCollection(false, function(o){
26470             return o.id || ("item" + (++autoId));
26471         });
26472         if(this.buttons){
26473             this.add.apply(this, this.buttons);
26474             delete this.buttons;
26475         }
26476     },
26477
26478     /**
26479      * Adds element(s) to the toolbar -- this function takes a variable number of 
26480      * arguments of mixed type and adds them to the toolbar.
26481      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26482      * <ul>
26483      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26484      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26485      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26486      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26487      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26488      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26489      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26490      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26491      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26492      * </ul>
26493      * @param {Mixed} arg2
26494      * @param {Mixed} etc.
26495      */
26496     add : function(){
26497         var a = arguments, l = a.length;
26498         for(var i = 0; i < l; i++){
26499             this._add(a[i]);
26500         }
26501     },
26502     // private..
26503     _add : function(el) {
26504         
26505         if (el.xtype) {
26506             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26507         }
26508         
26509         if (el.applyTo){ // some kind of form field
26510             return this.addField(el);
26511         } 
26512         if (el.render){ // some kind of Toolbar.Item
26513             return this.addItem(el);
26514         }
26515         if (typeof el == "string"){ // string
26516             if(el == "separator" || el == "-"){
26517                 return this.addSeparator();
26518             }
26519             if (el == " "){
26520                 return this.addSpacer();
26521             }
26522             if(el == "->"){
26523                 return this.addFill();
26524             }
26525             return this.addText(el);
26526             
26527         }
26528         if(el.tagName){ // element
26529             return this.addElement(el);
26530         }
26531         if(typeof el == "object"){ // must be button config?
26532             return this.addButton(el);
26533         }
26534         // and now what?!?!
26535         return false;
26536         
26537     },
26538     
26539     /**
26540      * Add an Xtype element
26541      * @param {Object} xtype Xtype Object
26542      * @return {Object} created Object
26543      */
26544     addxtype : function(e){
26545         return this.add(e);  
26546     },
26547     
26548     /**
26549      * Returns the Element for this toolbar.
26550      * @return {Roo.Element}
26551      */
26552     getEl : function(){
26553         return this.el;  
26554     },
26555     
26556     /**
26557      * Adds a separator
26558      * @return {Roo.Toolbar.Item} The separator item
26559      */
26560     addSeparator : function(){
26561         return this.addItem(new Roo.Toolbar.Separator());
26562     },
26563
26564     /**
26565      * Adds a spacer element
26566      * @return {Roo.Toolbar.Spacer} The spacer item
26567      */
26568     addSpacer : function(){
26569         return this.addItem(new Roo.Toolbar.Spacer());
26570     },
26571
26572     /**
26573      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26574      * @return {Roo.Toolbar.Fill} The fill item
26575      */
26576     addFill : function(){
26577         return this.addItem(new Roo.Toolbar.Fill());
26578     },
26579
26580     /**
26581      * Adds any standard HTML element to the toolbar
26582      * @param {String/HTMLElement/Element} el The element or id of the element to add
26583      * @return {Roo.Toolbar.Item} The element's item
26584      */
26585     addElement : function(el){
26586         return this.addItem(new Roo.Toolbar.Item(el));
26587     },
26588     /**
26589      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26590      * @type Roo.util.MixedCollection  
26591      */
26592     items : false,
26593      
26594     /**
26595      * Adds any Toolbar.Item or subclass
26596      * @param {Roo.Toolbar.Item} item
26597      * @return {Roo.Toolbar.Item} The item
26598      */
26599     addItem : function(item){
26600         var td = this.nextBlock();
26601         item.render(td);
26602         this.items.add(item);
26603         return item;
26604     },
26605     
26606     /**
26607      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26608      * @param {Object/Array} config A button config or array of configs
26609      * @return {Roo.Toolbar.Button/Array}
26610      */
26611     addButton : function(config){
26612         if(config instanceof Array){
26613             var buttons = [];
26614             for(var i = 0, len = config.length; i < len; i++) {
26615                 buttons.push(this.addButton(config[i]));
26616             }
26617             return buttons;
26618         }
26619         var b = config;
26620         if(!(config instanceof Roo.Toolbar.Button)){
26621             b = config.split ?
26622                 new Roo.Toolbar.SplitButton(config) :
26623                 new Roo.Toolbar.Button(config);
26624         }
26625         var td = this.nextBlock();
26626         b.render(td);
26627         this.items.add(b);
26628         return b;
26629     },
26630     
26631     /**
26632      * Adds text to the toolbar
26633      * @param {String} text The text to add
26634      * @return {Roo.Toolbar.Item} The element's item
26635      */
26636     addText : function(text){
26637         return this.addItem(new Roo.Toolbar.TextItem(text));
26638     },
26639     
26640     /**
26641      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26642      * @param {Number} index The index where the item is to be inserted
26643      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26644      * @return {Roo.Toolbar.Button/Item}
26645      */
26646     insertButton : function(index, item){
26647         if(item instanceof Array){
26648             var buttons = [];
26649             for(var i = 0, len = item.length; i < len; i++) {
26650                buttons.push(this.insertButton(index + i, item[i]));
26651             }
26652             return buttons;
26653         }
26654         if (!(item instanceof Roo.Toolbar.Button)){
26655            item = new Roo.Toolbar.Button(item);
26656         }
26657         var td = document.createElement("td");
26658         this.tr.insertBefore(td, this.tr.childNodes[index]);
26659         item.render(td);
26660         this.items.insert(index, item);
26661         return item;
26662     },
26663     
26664     /**
26665      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26666      * @param {Object} config
26667      * @return {Roo.Toolbar.Item} The element's item
26668      */
26669     addDom : function(config, returnEl){
26670         var td = this.nextBlock();
26671         Roo.DomHelper.overwrite(td, config);
26672         var ti = new Roo.Toolbar.Item(td.firstChild);
26673         ti.render(td);
26674         this.items.add(ti);
26675         return ti;
26676     },
26677
26678     /**
26679      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26680      * @type Roo.util.MixedCollection  
26681      */
26682     fields : false,
26683     
26684     /**
26685      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26686      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26687      * @param {Roo.form.Field} field
26688      * @return {Roo.ToolbarItem}
26689      */
26690      
26691       
26692     addField : function(field) {
26693         if (!this.fields) {
26694             var autoId = 0;
26695             this.fields = new Roo.util.MixedCollection(false, function(o){
26696                 return o.id || ("item" + (++autoId));
26697             });
26698
26699         }
26700         
26701         var td = this.nextBlock();
26702         field.render(td);
26703         var ti = new Roo.Toolbar.Item(td.firstChild);
26704         ti.render(td);
26705         this.items.add(ti);
26706         this.fields.add(field);
26707         return ti;
26708     },
26709     /**
26710      * Hide the toolbar
26711      * @method hide
26712      */
26713      
26714       
26715     hide : function()
26716     {
26717         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26718         this.el.child('div').hide();
26719     },
26720     /**
26721      * Show the toolbar
26722      * @method show
26723      */
26724     show : function()
26725     {
26726         this.el.child('div').show();
26727     },
26728       
26729     // private
26730     nextBlock : function(){
26731         var td = document.createElement("td");
26732         this.tr.appendChild(td);
26733         return td;
26734     },
26735
26736     // private
26737     destroy : function(){
26738         if(this.items){ // rendered?
26739             Roo.destroy.apply(Roo, this.items.items);
26740         }
26741         if(this.fields){ // rendered?
26742             Roo.destroy.apply(Roo, this.fields.items);
26743         }
26744         Roo.Element.uncache(this.el, this.tr);
26745     }
26746 };
26747
26748 /**
26749  * @class Roo.Toolbar.Item
26750  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26751  * @constructor
26752  * Creates a new Item
26753  * @param {HTMLElement} el 
26754  */
26755 Roo.Toolbar.Item = function(el){
26756     this.el = Roo.getDom(el);
26757     this.id = Roo.id(this.el);
26758     this.hidden = false;
26759 };
26760
26761 Roo.Toolbar.Item.prototype = {
26762     
26763     /**
26764      * Get this item's HTML Element
26765      * @return {HTMLElement}
26766      */
26767     getEl : function(){
26768        return this.el;  
26769     },
26770
26771     // private
26772     render : function(td){
26773         this.td = td;
26774         td.appendChild(this.el);
26775     },
26776     
26777     /**
26778      * Removes and destroys this item.
26779      */
26780     destroy : function(){
26781         this.td.parentNode.removeChild(this.td);
26782     },
26783     
26784     /**
26785      * Shows this item.
26786      */
26787     show: function(){
26788         this.hidden = false;
26789         this.td.style.display = "";
26790     },
26791     
26792     /**
26793      * Hides this item.
26794      */
26795     hide: function(){
26796         this.hidden = true;
26797         this.td.style.display = "none";
26798     },
26799     
26800     /**
26801      * Convenience function for boolean show/hide.
26802      * @param {Boolean} visible true to show/false to hide
26803      */
26804     setVisible: function(visible){
26805         if(visible) {
26806             this.show();
26807         }else{
26808             this.hide();
26809         }
26810     },
26811     
26812     /**
26813      * Try to focus this item.
26814      */
26815     focus : function(){
26816         Roo.fly(this.el).focus();
26817     },
26818     
26819     /**
26820      * Disables this item.
26821      */
26822     disable : function(){
26823         Roo.fly(this.td).addClass("x-item-disabled");
26824         this.disabled = true;
26825         this.el.disabled = true;
26826     },
26827     
26828     /**
26829      * Enables this item.
26830      */
26831     enable : function(){
26832         Roo.fly(this.td).removeClass("x-item-disabled");
26833         this.disabled = false;
26834         this.el.disabled = false;
26835     }
26836 };
26837
26838
26839 /**
26840  * @class Roo.Toolbar.Separator
26841  * @extends Roo.Toolbar.Item
26842  * A simple toolbar separator class
26843  * @constructor
26844  * Creates a new Separator
26845  */
26846 Roo.Toolbar.Separator = function(){
26847     var s = document.createElement("span");
26848     s.className = "ytb-sep";
26849     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26850 };
26851 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26852     enable:Roo.emptyFn,
26853     disable:Roo.emptyFn,
26854     focus:Roo.emptyFn
26855 });
26856
26857 /**
26858  * @class Roo.Toolbar.Spacer
26859  * @extends Roo.Toolbar.Item
26860  * A simple element that adds extra horizontal space to a toolbar.
26861  * @constructor
26862  * Creates a new Spacer
26863  */
26864 Roo.Toolbar.Spacer = function(){
26865     var s = document.createElement("div");
26866     s.className = "ytb-spacer";
26867     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26868 };
26869 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26870     enable:Roo.emptyFn,
26871     disable:Roo.emptyFn,
26872     focus:Roo.emptyFn
26873 });
26874
26875 /**
26876  * @class Roo.Toolbar.Fill
26877  * @extends Roo.Toolbar.Spacer
26878  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26879  * @constructor
26880  * Creates a new Spacer
26881  */
26882 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26883     // private
26884     render : function(td){
26885         td.style.width = '100%';
26886         Roo.Toolbar.Fill.superclass.render.call(this, td);
26887     }
26888 });
26889
26890 /**
26891  * @class Roo.Toolbar.TextItem
26892  * @extends Roo.Toolbar.Item
26893  * A simple class that renders text directly into a toolbar.
26894  * @constructor
26895  * Creates a new TextItem
26896  * @param {String} text
26897  */
26898 Roo.Toolbar.TextItem = function(text){
26899     if (typeof(text) == 'object') {
26900         text = text.text;
26901     }
26902     var s = document.createElement("span");
26903     s.className = "ytb-text";
26904     s.innerHTML = text;
26905     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26906 };
26907 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26908     enable:Roo.emptyFn,
26909     disable:Roo.emptyFn,
26910     focus:Roo.emptyFn
26911 });
26912
26913 /**
26914  * @class Roo.Toolbar.Button
26915  * @extends Roo.Button
26916  * A button that renders into a toolbar.
26917  * @constructor
26918  * Creates a new Button
26919  * @param {Object} config A standard {@link Roo.Button} config object
26920  */
26921 Roo.Toolbar.Button = function(config){
26922     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26923 };
26924 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26925     render : function(td){
26926         this.td = td;
26927         Roo.Toolbar.Button.superclass.render.call(this, td);
26928     },
26929     
26930     /**
26931      * Removes and destroys this button
26932      */
26933     destroy : function(){
26934         Roo.Toolbar.Button.superclass.destroy.call(this);
26935         this.td.parentNode.removeChild(this.td);
26936     },
26937     
26938     /**
26939      * Shows this button
26940      */
26941     show: function(){
26942         this.hidden = false;
26943         this.td.style.display = "";
26944     },
26945     
26946     /**
26947      * Hides this button
26948      */
26949     hide: function(){
26950         this.hidden = true;
26951         this.td.style.display = "none";
26952     },
26953
26954     /**
26955      * Disables this item
26956      */
26957     disable : function(){
26958         Roo.fly(this.td).addClass("x-item-disabled");
26959         this.disabled = true;
26960     },
26961
26962     /**
26963      * Enables this item
26964      */
26965     enable : function(){
26966         Roo.fly(this.td).removeClass("x-item-disabled");
26967         this.disabled = false;
26968     }
26969 });
26970 // backwards compat
26971 Roo.ToolbarButton = Roo.Toolbar.Button;
26972
26973 /**
26974  * @class Roo.Toolbar.SplitButton
26975  * @extends Roo.SplitButton
26976  * A menu button that renders into a toolbar.
26977  * @constructor
26978  * Creates a new SplitButton
26979  * @param {Object} config A standard {@link Roo.SplitButton} config object
26980  */
26981 Roo.Toolbar.SplitButton = function(config){
26982     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26983 };
26984 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26985     render : function(td){
26986         this.td = td;
26987         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26988     },
26989     
26990     /**
26991      * Removes and destroys this button
26992      */
26993     destroy : function(){
26994         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26995         this.td.parentNode.removeChild(this.td);
26996     },
26997     
26998     /**
26999      * Shows this button
27000      */
27001     show: function(){
27002         this.hidden = false;
27003         this.td.style.display = "";
27004     },
27005     
27006     /**
27007      * Hides this button
27008      */
27009     hide: function(){
27010         this.hidden = true;
27011         this.td.style.display = "none";
27012     }
27013 });
27014
27015 // backwards compat
27016 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27017  * Based on:
27018  * Ext JS Library 1.1.1
27019  * Copyright(c) 2006-2007, Ext JS, LLC.
27020  *
27021  * Originally Released Under LGPL - original licence link has changed is not relivant.
27022  *
27023  * Fork - LGPL
27024  * <script type="text/javascript">
27025  */
27026  
27027 /**
27028  * @class Roo.PagingToolbar
27029  * @extends Roo.Toolbar
27030  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27031  * @constructor
27032  * Create a new PagingToolbar
27033  * @param {Object} config The config object
27034  */
27035 Roo.PagingToolbar = function(el, ds, config)
27036 {
27037     // old args format still supported... - xtype is prefered..
27038     if (typeof(el) == 'object' && el.xtype) {
27039         // created from xtype...
27040         config = el;
27041         ds = el.dataSource;
27042         el = config.container;
27043     }
27044     
27045     
27046     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27047     this.ds = ds;
27048     this.cursor = 0;
27049     this.renderButtons(this.el);
27050     this.bind(ds);
27051 };
27052
27053 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27054     /**
27055      * @cfg {Roo.data.Store} dataSource
27056      * The underlying data store providing the paged data
27057      */
27058     /**
27059      * @cfg {String/HTMLElement/Element} container
27060      * container The id or element that will contain the toolbar
27061      */
27062     /**
27063      * @cfg {Boolean} displayInfo
27064      * True to display the displayMsg (defaults to false)
27065      */
27066     /**
27067      * @cfg {Number} pageSize
27068      * The number of records to display per page (defaults to 20)
27069      */
27070     pageSize: 20,
27071     /**
27072      * @cfg {String} displayMsg
27073      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27074      */
27075     displayMsg : 'Displaying {0} - {1} of {2}',
27076     /**
27077      * @cfg {String} emptyMsg
27078      * The message to display when no records are found (defaults to "No data to display")
27079      */
27080     emptyMsg : 'No data to display',
27081     /**
27082      * Customizable piece of the default paging text (defaults to "Page")
27083      * @type String
27084      */
27085     beforePageText : "Page",
27086     /**
27087      * Customizable piece of the default paging text (defaults to "of %0")
27088      * @type String
27089      */
27090     afterPageText : "of {0}",
27091     /**
27092      * Customizable piece of the default paging text (defaults to "First Page")
27093      * @type String
27094      */
27095     firstText : "First Page",
27096     /**
27097      * Customizable piece of the default paging text (defaults to "Previous Page")
27098      * @type String
27099      */
27100     prevText : "Previous Page",
27101     /**
27102      * Customizable piece of the default paging text (defaults to "Next Page")
27103      * @type String
27104      */
27105     nextText : "Next Page",
27106     /**
27107      * Customizable piece of the default paging text (defaults to "Last Page")
27108      * @type String
27109      */
27110     lastText : "Last Page",
27111     /**
27112      * Customizable piece of the default paging text (defaults to "Refresh")
27113      * @type String
27114      */
27115     refreshText : "Refresh",
27116
27117     // private
27118     renderButtons : function(el){
27119         Roo.PagingToolbar.superclass.render.call(this, el);
27120         this.first = this.addButton({
27121             tooltip: this.firstText,
27122             cls: "x-btn-icon x-grid-page-first",
27123             disabled: true,
27124             handler: this.onClick.createDelegate(this, ["first"])
27125         });
27126         this.prev = this.addButton({
27127             tooltip: this.prevText,
27128             cls: "x-btn-icon x-grid-page-prev",
27129             disabled: true,
27130             handler: this.onClick.createDelegate(this, ["prev"])
27131         });
27132         this.addSeparator();
27133         this.add(this.beforePageText);
27134         this.field = Roo.get(this.addDom({
27135            tag: "input",
27136            type: "text",
27137            size: "3",
27138            value: "1",
27139            cls: "x-grid-page-number"
27140         }).el);
27141         this.field.on("keydown", this.onPagingKeydown, this);
27142         this.field.on("focus", function(){this.dom.select();});
27143         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27144         this.field.setHeight(18);
27145         this.addSeparator();
27146         this.next = this.addButton({
27147             tooltip: this.nextText,
27148             cls: "x-btn-icon x-grid-page-next",
27149             disabled: true,
27150             handler: this.onClick.createDelegate(this, ["next"])
27151         });
27152         this.last = this.addButton({
27153             tooltip: this.lastText,
27154             cls: "x-btn-icon x-grid-page-last",
27155             disabled: true,
27156             handler: this.onClick.createDelegate(this, ["last"])
27157         });
27158         this.addSeparator();
27159         this.loading = this.addButton({
27160             tooltip: this.refreshText,
27161             cls: "x-btn-icon x-grid-loading",
27162             handler: this.onClick.createDelegate(this, ["refresh"])
27163         });
27164
27165         if(this.displayInfo){
27166             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27167         }
27168     },
27169
27170     // private
27171     updateInfo : function(){
27172         if(this.displayEl){
27173             var count = this.ds.getCount();
27174             var msg = count == 0 ?
27175                 this.emptyMsg :
27176                 String.format(
27177                     this.displayMsg,
27178                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27179                 );
27180             this.displayEl.update(msg);
27181         }
27182     },
27183
27184     // private
27185     onLoad : function(ds, r, o){
27186        this.cursor = o.params ? o.params.start : 0;
27187        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27188
27189        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27190        this.field.dom.value = ap;
27191        this.first.setDisabled(ap == 1);
27192        this.prev.setDisabled(ap == 1);
27193        this.next.setDisabled(ap == ps);
27194        this.last.setDisabled(ap == ps);
27195        this.loading.enable();
27196        this.updateInfo();
27197     },
27198
27199     // private
27200     getPageData : function(){
27201         var total = this.ds.getTotalCount();
27202         return {
27203             total : total,
27204             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27205             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27206         };
27207     },
27208
27209     // private
27210     onLoadError : function(){
27211         this.loading.enable();
27212     },
27213
27214     // private
27215     onPagingKeydown : function(e){
27216         var k = e.getKey();
27217         var d = this.getPageData();
27218         if(k == e.RETURN){
27219             var v = this.field.dom.value, pageNum;
27220             if(!v || isNaN(pageNum = parseInt(v, 10))){
27221                 this.field.dom.value = d.activePage;
27222                 return;
27223             }
27224             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27225             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27226             e.stopEvent();
27227         }
27228         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))
27229         {
27230           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27231           this.field.dom.value = pageNum;
27232           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27233           e.stopEvent();
27234         }
27235         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27236         {
27237           var v = this.field.dom.value, pageNum; 
27238           var increment = (e.shiftKey) ? 10 : 1;
27239           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27240             increment *= -1;
27241           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27242             this.field.dom.value = d.activePage;
27243             return;
27244           }
27245           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27246           {
27247             this.field.dom.value = parseInt(v, 10) + increment;
27248             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27249             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27250           }
27251           e.stopEvent();
27252         }
27253     },
27254
27255     // private
27256     beforeLoad : function(){
27257         if(this.loading){
27258             this.loading.disable();
27259         }
27260     },
27261
27262     // private
27263     onClick : function(which){
27264         var ds = this.ds;
27265         switch(which){
27266             case "first":
27267                 ds.load({params:{start: 0, limit: this.pageSize}});
27268             break;
27269             case "prev":
27270                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27271             break;
27272             case "next":
27273                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27274             break;
27275             case "last":
27276                 var total = ds.getTotalCount();
27277                 var extra = total % this.pageSize;
27278                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27279                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27280             break;
27281             case "refresh":
27282                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27283             break;
27284         }
27285     },
27286
27287     /**
27288      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27289      * @param {Roo.data.Store} store The data store to unbind
27290      */
27291     unbind : function(ds){
27292         ds.un("beforeload", this.beforeLoad, this);
27293         ds.un("load", this.onLoad, this);
27294         ds.un("loadexception", this.onLoadError, this);
27295         ds.un("remove", this.updateInfo, this);
27296         ds.un("add", this.updateInfo, this);
27297         this.ds = undefined;
27298     },
27299
27300     /**
27301      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27302      * @param {Roo.data.Store} store The data store to bind
27303      */
27304     bind : function(ds){
27305         ds.on("beforeload", this.beforeLoad, this);
27306         ds.on("load", this.onLoad, this);
27307         ds.on("loadexception", this.onLoadError, this);
27308         ds.on("remove", this.updateInfo, this);
27309         ds.on("add", this.updateInfo, this);
27310         this.ds = ds;
27311     }
27312 });/*
27313  * Based on:
27314  * Ext JS Library 1.1.1
27315  * Copyright(c) 2006-2007, Ext JS, LLC.
27316  *
27317  * Originally Released Under LGPL - original licence link has changed is not relivant.
27318  *
27319  * Fork - LGPL
27320  * <script type="text/javascript">
27321  */
27322
27323 /**
27324  * @class Roo.Resizable
27325  * @extends Roo.util.Observable
27326  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27327  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27328  * 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
27329  * the element will be wrapped for you automatically.</p>
27330  * <p>Here is the list of valid resize handles:</p>
27331  * <pre>
27332 Value   Description
27333 ------  -------------------
27334  'n'     north
27335  's'     south
27336  'e'     east
27337  'w'     west
27338  'nw'    northwest
27339  'sw'    southwest
27340  'se'    southeast
27341  'ne'    northeast
27342  'all'   all
27343 </pre>
27344  * <p>Here's an example showing the creation of a typical Resizable:</p>
27345  * <pre><code>
27346 var resizer = new Roo.Resizable("element-id", {
27347     handles: 'all',
27348     minWidth: 200,
27349     minHeight: 100,
27350     maxWidth: 500,
27351     maxHeight: 400,
27352     pinned: true
27353 });
27354 resizer.on("resize", myHandler);
27355 </code></pre>
27356  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27357  * resizer.east.setDisplayed(false);</p>
27358  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27359  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27360  * resize operation's new size (defaults to [0, 0])
27361  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27362  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27363  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27364  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27365  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27366  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27367  * @cfg {Number} width The width of the element in pixels (defaults to null)
27368  * @cfg {Number} height The height of the element in pixels (defaults to null)
27369  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27370  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27371  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27372  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27373  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27374  * in favor of the handles config option (defaults to false)
27375  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27376  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27377  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27378  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27379  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27380  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27381  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27382  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27383  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27384  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27385  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27386  * @constructor
27387  * Create a new resizable component
27388  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27389  * @param {Object} config configuration options
27390   */
27391 Roo.Resizable = function(el, config){
27392     this.el = Roo.get(el);
27393
27394     if(config && config.wrap){
27395         config.resizeChild = this.el;
27396         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27397         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27398         this.el.setStyle("overflow", "hidden");
27399         this.el.setPositioning(config.resizeChild.getPositioning());
27400         config.resizeChild.clearPositioning();
27401         if(!config.width || !config.height){
27402             var csize = config.resizeChild.getSize();
27403             this.el.setSize(csize.width, csize.height);
27404         }
27405         if(config.pinned && !config.adjustments){
27406             config.adjustments = "auto";
27407         }
27408     }
27409
27410     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27411     this.proxy.unselectable();
27412     this.proxy.enableDisplayMode('block');
27413
27414     Roo.apply(this, config);
27415
27416     if(this.pinned){
27417         this.disableTrackOver = true;
27418         this.el.addClass("x-resizable-pinned");
27419     }
27420     // if the element isn't positioned, make it relative
27421     var position = this.el.getStyle("position");
27422     if(position != "absolute" && position != "fixed"){
27423         this.el.setStyle("position", "relative");
27424     }
27425     if(!this.handles){ // no handles passed, must be legacy style
27426         this.handles = 's,e,se';
27427         if(this.multiDirectional){
27428             this.handles += ',n,w';
27429         }
27430     }
27431     if(this.handles == "all"){
27432         this.handles = "n s e w ne nw se sw";
27433     }
27434     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27435     var ps = Roo.Resizable.positions;
27436     for(var i = 0, len = hs.length; i < len; i++){
27437         if(hs[i] && ps[hs[i]]){
27438             var pos = ps[hs[i]];
27439             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27440         }
27441     }
27442     // legacy
27443     this.corner = this.southeast;
27444
27445     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27446         this.updateBox = true;
27447     }
27448
27449     this.activeHandle = null;
27450
27451     if(this.resizeChild){
27452         if(typeof this.resizeChild == "boolean"){
27453             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27454         }else{
27455             this.resizeChild = Roo.get(this.resizeChild, true);
27456         }
27457     }
27458
27459     if(this.adjustments == "auto"){
27460         var rc = this.resizeChild;
27461         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27462         if(rc && (hw || hn)){
27463             rc.position("relative");
27464             rc.setLeft(hw ? hw.el.getWidth() : 0);
27465             rc.setTop(hn ? hn.el.getHeight() : 0);
27466         }
27467         this.adjustments = [
27468             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27469             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27470         ];
27471     }
27472
27473     if(this.draggable){
27474         this.dd = this.dynamic ?
27475             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27476         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27477     }
27478
27479     // public events
27480     this.addEvents({
27481         /**
27482          * @event beforeresize
27483          * Fired before resize is allowed. Set enabled to false to cancel resize.
27484          * @param {Roo.Resizable} this
27485          * @param {Roo.EventObject} e The mousedown event
27486          */
27487         "beforeresize" : true,
27488         /**
27489          * @event resize
27490          * Fired after a resize.
27491          * @param {Roo.Resizable} this
27492          * @param {Number} width The new width
27493          * @param {Number} height The new height
27494          * @param {Roo.EventObject} e The mouseup event
27495          */
27496         "resize" : true
27497     });
27498
27499     if(this.width !== null && this.height !== null){
27500         this.resizeTo(this.width, this.height);
27501     }else{
27502         this.updateChildSize();
27503     }
27504     if(Roo.isIE){
27505         this.el.dom.style.zoom = 1;
27506     }
27507     Roo.Resizable.superclass.constructor.call(this);
27508 };
27509
27510 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27511         resizeChild : false,
27512         adjustments : [0, 0],
27513         minWidth : 5,
27514         minHeight : 5,
27515         maxWidth : 10000,
27516         maxHeight : 10000,
27517         enabled : true,
27518         animate : false,
27519         duration : .35,
27520         dynamic : false,
27521         handles : false,
27522         multiDirectional : false,
27523         disableTrackOver : false,
27524         easing : 'easeOutStrong',
27525         widthIncrement : 0,
27526         heightIncrement : 0,
27527         pinned : false,
27528         width : null,
27529         height : null,
27530         preserveRatio : false,
27531         transparent: false,
27532         minX: 0,
27533         minY: 0,
27534         draggable: false,
27535
27536         /**
27537          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27538          */
27539         constrainTo: undefined,
27540         /**
27541          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27542          */
27543         resizeRegion: undefined,
27544
27545
27546     /**
27547      * Perform a manual resize
27548      * @param {Number} width
27549      * @param {Number} height
27550      */
27551     resizeTo : function(width, height){
27552         this.el.setSize(width, height);
27553         this.updateChildSize();
27554         this.fireEvent("resize", this, width, height, null);
27555     },
27556
27557     // private
27558     startSizing : function(e, handle){
27559         this.fireEvent("beforeresize", this, e);
27560         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27561
27562             if(!this.overlay){
27563                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27564                 this.overlay.unselectable();
27565                 this.overlay.enableDisplayMode("block");
27566                 this.overlay.on("mousemove", this.onMouseMove, this);
27567                 this.overlay.on("mouseup", this.onMouseUp, this);
27568             }
27569             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27570
27571             this.resizing = true;
27572             this.startBox = this.el.getBox();
27573             this.startPoint = e.getXY();
27574             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27575                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27576
27577             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27578             this.overlay.show();
27579
27580             if(this.constrainTo) {
27581                 var ct = Roo.get(this.constrainTo);
27582                 this.resizeRegion = ct.getRegion().adjust(
27583                     ct.getFrameWidth('t'),
27584                     ct.getFrameWidth('l'),
27585                     -ct.getFrameWidth('b'),
27586                     -ct.getFrameWidth('r')
27587                 );
27588             }
27589
27590             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27591             this.proxy.show();
27592             this.proxy.setBox(this.startBox);
27593             if(!this.dynamic){
27594                 this.proxy.setStyle('visibility', 'visible');
27595             }
27596         }
27597     },
27598
27599     // private
27600     onMouseDown : function(handle, e){
27601         if(this.enabled){
27602             e.stopEvent();
27603             this.activeHandle = handle;
27604             this.startSizing(e, handle);
27605         }
27606     },
27607
27608     // private
27609     onMouseUp : function(e){
27610         var size = this.resizeElement();
27611         this.resizing = false;
27612         this.handleOut();
27613         this.overlay.hide();
27614         this.proxy.hide();
27615         this.fireEvent("resize", this, size.width, size.height, e);
27616     },
27617
27618     // private
27619     updateChildSize : function(){
27620         if(this.resizeChild){
27621             var el = this.el;
27622             var child = this.resizeChild;
27623             var adj = this.adjustments;
27624             if(el.dom.offsetWidth){
27625                 var b = el.getSize(true);
27626                 child.setSize(b.width+adj[0], b.height+adj[1]);
27627             }
27628             // Second call here for IE
27629             // The first call enables instant resizing and
27630             // the second call corrects scroll bars if they
27631             // exist
27632             if(Roo.isIE){
27633                 setTimeout(function(){
27634                     if(el.dom.offsetWidth){
27635                         var b = el.getSize(true);
27636                         child.setSize(b.width+adj[0], b.height+adj[1]);
27637                     }
27638                 }, 10);
27639             }
27640         }
27641     },
27642
27643     // private
27644     snap : function(value, inc, min){
27645         if(!inc || !value) return value;
27646         var newValue = value;
27647         var m = value % inc;
27648         if(m > 0){
27649             if(m > (inc/2)){
27650                 newValue = value + (inc-m);
27651             }else{
27652                 newValue = value - m;
27653             }
27654         }
27655         return Math.max(min, newValue);
27656     },
27657
27658     // private
27659     resizeElement : function(){
27660         var box = this.proxy.getBox();
27661         if(this.updateBox){
27662             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27663         }else{
27664             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27665         }
27666         this.updateChildSize();
27667         if(!this.dynamic){
27668             this.proxy.hide();
27669         }
27670         return box;
27671     },
27672
27673     // private
27674     constrain : function(v, diff, m, mx){
27675         if(v - diff < m){
27676             diff = v - m;
27677         }else if(v - diff > mx){
27678             diff = mx - v;
27679         }
27680         return diff;
27681     },
27682
27683     // private
27684     onMouseMove : function(e){
27685         if(this.enabled){
27686             try{// try catch so if something goes wrong the user doesn't get hung
27687
27688             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27689                 return;
27690             }
27691
27692             //var curXY = this.startPoint;
27693             var curSize = this.curSize || this.startBox;
27694             var x = this.startBox.x, y = this.startBox.y;
27695             var ox = x, oy = y;
27696             var w = curSize.width, h = curSize.height;
27697             var ow = w, oh = h;
27698             var mw = this.minWidth, mh = this.minHeight;
27699             var mxw = this.maxWidth, mxh = this.maxHeight;
27700             var wi = this.widthIncrement;
27701             var hi = this.heightIncrement;
27702
27703             var eventXY = e.getXY();
27704             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27705             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27706
27707             var pos = this.activeHandle.position;
27708
27709             switch(pos){
27710                 case "east":
27711                     w += diffX;
27712                     w = Math.min(Math.max(mw, w), mxw);
27713                     break;
27714                 case "south":
27715                     h += diffY;
27716                     h = Math.min(Math.max(mh, h), mxh);
27717                     break;
27718                 case "southeast":
27719                     w += diffX;
27720                     h += diffY;
27721                     w = Math.min(Math.max(mw, w), mxw);
27722                     h = Math.min(Math.max(mh, h), mxh);
27723                     break;
27724                 case "north":
27725                     diffY = this.constrain(h, diffY, mh, mxh);
27726                     y += diffY;
27727                     h -= diffY;
27728                     break;
27729                 case "west":
27730                     diffX = this.constrain(w, diffX, mw, mxw);
27731                     x += diffX;
27732                     w -= diffX;
27733                     break;
27734                 case "northeast":
27735                     w += diffX;
27736                     w = Math.min(Math.max(mw, w), mxw);
27737                     diffY = this.constrain(h, diffY, mh, mxh);
27738                     y += diffY;
27739                     h -= diffY;
27740                     break;
27741                 case "northwest":
27742                     diffX = this.constrain(w, diffX, mw, mxw);
27743                     diffY = this.constrain(h, diffY, mh, mxh);
27744                     y += diffY;
27745                     h -= diffY;
27746                     x += diffX;
27747                     w -= diffX;
27748                     break;
27749                case "southwest":
27750                     diffX = this.constrain(w, diffX, mw, mxw);
27751                     h += diffY;
27752                     h = Math.min(Math.max(mh, h), mxh);
27753                     x += diffX;
27754                     w -= diffX;
27755                     break;
27756             }
27757
27758             var sw = this.snap(w, wi, mw);
27759             var sh = this.snap(h, hi, mh);
27760             if(sw != w || sh != h){
27761                 switch(pos){
27762                     case "northeast":
27763                         y -= sh - h;
27764                     break;
27765                     case "north":
27766                         y -= sh - h;
27767                         break;
27768                     case "southwest":
27769                         x -= sw - w;
27770                     break;
27771                     case "west":
27772                         x -= sw - w;
27773                         break;
27774                     case "northwest":
27775                         x -= sw - w;
27776                         y -= sh - h;
27777                     break;
27778                 }
27779                 w = sw;
27780                 h = sh;
27781             }
27782
27783             if(this.preserveRatio){
27784                 switch(pos){
27785                     case "southeast":
27786                     case "east":
27787                         h = oh * (w/ow);
27788                         h = Math.min(Math.max(mh, h), mxh);
27789                         w = ow * (h/oh);
27790                        break;
27791                     case "south":
27792                         w = ow * (h/oh);
27793                         w = Math.min(Math.max(mw, w), mxw);
27794                         h = oh * (w/ow);
27795                         break;
27796                     case "northeast":
27797                         w = ow * (h/oh);
27798                         w = Math.min(Math.max(mw, w), mxw);
27799                         h = oh * (w/ow);
27800                     break;
27801                     case "north":
27802                         var tw = w;
27803                         w = ow * (h/oh);
27804                         w = Math.min(Math.max(mw, w), mxw);
27805                         h = oh * (w/ow);
27806                         x += (tw - w) / 2;
27807                         break;
27808                     case "southwest":
27809                         h = oh * (w/ow);
27810                         h = Math.min(Math.max(mh, h), mxh);
27811                         var tw = w;
27812                         w = ow * (h/oh);
27813                         x += tw - w;
27814                         break;
27815                     case "west":
27816                         var th = h;
27817                         h = oh * (w/ow);
27818                         h = Math.min(Math.max(mh, h), mxh);
27819                         y += (th - h) / 2;
27820                         var tw = w;
27821                         w = ow * (h/oh);
27822                         x += tw - w;
27823                        break;
27824                     case "northwest":
27825                         var tw = w;
27826                         var th = h;
27827                         h = oh * (w/ow);
27828                         h = Math.min(Math.max(mh, h), mxh);
27829                         w = ow * (h/oh);
27830                         y += th - h;
27831                          x += tw - w;
27832                        break;
27833
27834                 }
27835             }
27836             this.proxy.setBounds(x, y, w, h);
27837             if(this.dynamic){
27838                 this.resizeElement();
27839             }
27840             }catch(e){}
27841         }
27842     },
27843
27844     // private
27845     handleOver : function(){
27846         if(this.enabled){
27847             this.el.addClass("x-resizable-over");
27848         }
27849     },
27850
27851     // private
27852     handleOut : function(){
27853         if(!this.resizing){
27854             this.el.removeClass("x-resizable-over");
27855         }
27856     },
27857
27858     /**
27859      * Returns the element this component is bound to.
27860      * @return {Roo.Element}
27861      */
27862     getEl : function(){
27863         return this.el;
27864     },
27865
27866     /**
27867      * Returns the resizeChild element (or null).
27868      * @return {Roo.Element}
27869      */
27870     getResizeChild : function(){
27871         return this.resizeChild;
27872     },
27873
27874     /**
27875      * Destroys this resizable. If the element was wrapped and
27876      * removeEl is not true then the element remains.
27877      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27878      */
27879     destroy : function(removeEl){
27880         this.proxy.remove();
27881         if(this.overlay){
27882             this.overlay.removeAllListeners();
27883             this.overlay.remove();
27884         }
27885         var ps = Roo.Resizable.positions;
27886         for(var k in ps){
27887             if(typeof ps[k] != "function" && this[ps[k]]){
27888                 var h = this[ps[k]];
27889                 h.el.removeAllListeners();
27890                 h.el.remove();
27891             }
27892         }
27893         if(removeEl){
27894             this.el.update("");
27895             this.el.remove();
27896         }
27897     }
27898 });
27899
27900 // private
27901 // hash to map config positions to true positions
27902 Roo.Resizable.positions = {
27903     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27904 };
27905
27906 // private
27907 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27908     if(!this.tpl){
27909         // only initialize the template if resizable is used
27910         var tpl = Roo.DomHelper.createTemplate(
27911             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27912         );
27913         tpl.compile();
27914         Roo.Resizable.Handle.prototype.tpl = tpl;
27915     }
27916     this.position = pos;
27917     this.rz = rz;
27918     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27919     this.el.unselectable();
27920     if(transparent){
27921         this.el.setOpacity(0);
27922     }
27923     this.el.on("mousedown", this.onMouseDown, this);
27924     if(!disableTrackOver){
27925         this.el.on("mouseover", this.onMouseOver, this);
27926         this.el.on("mouseout", this.onMouseOut, this);
27927     }
27928 };
27929
27930 // private
27931 Roo.Resizable.Handle.prototype = {
27932     afterResize : function(rz){
27933         // do nothing
27934     },
27935     // private
27936     onMouseDown : function(e){
27937         this.rz.onMouseDown(this, e);
27938     },
27939     // private
27940     onMouseOver : function(e){
27941         this.rz.handleOver(this, e);
27942     },
27943     // private
27944     onMouseOut : function(e){
27945         this.rz.handleOut(this, e);
27946     }
27947 };/*
27948  * Based on:
27949  * Ext JS Library 1.1.1
27950  * Copyright(c) 2006-2007, Ext JS, LLC.
27951  *
27952  * Originally Released Under LGPL - original licence link has changed is not relivant.
27953  *
27954  * Fork - LGPL
27955  * <script type="text/javascript">
27956  */
27957
27958 /**
27959  * @class Roo.Editor
27960  * @extends Roo.Component
27961  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27962  * @constructor
27963  * Create a new Editor
27964  * @param {Roo.form.Field} field The Field object (or descendant)
27965  * @param {Object} config The config object
27966  */
27967 Roo.Editor = function(field, config){
27968     Roo.Editor.superclass.constructor.call(this, config);
27969     this.field = field;
27970     this.addEvents({
27971         /**
27972              * @event beforestartedit
27973              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27974              * false from the handler of this event.
27975              * @param {Editor} this
27976              * @param {Roo.Element} boundEl The underlying element bound to this editor
27977              * @param {Mixed} value The field value being set
27978              */
27979         "beforestartedit" : true,
27980         /**
27981              * @event startedit
27982              * Fires when this editor is displayed
27983              * @param {Roo.Element} boundEl The underlying element bound to this editor
27984              * @param {Mixed} value The starting field value
27985              */
27986         "startedit" : true,
27987         /**
27988              * @event beforecomplete
27989              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27990              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27991              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27992              * event will not fire since no edit actually occurred.
27993              * @param {Editor} this
27994              * @param {Mixed} value The current field value
27995              * @param {Mixed} startValue The original field value
27996              */
27997         "beforecomplete" : true,
27998         /**
27999              * @event complete
28000              * Fires after editing is complete and any changed value has been written to the underlying field.
28001              * @param {Editor} this
28002              * @param {Mixed} value The current field value
28003              * @param {Mixed} startValue The original field value
28004              */
28005         "complete" : true,
28006         /**
28007          * @event specialkey
28008          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28009          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28010          * @param {Roo.form.Field} this
28011          * @param {Roo.EventObject} e The event object
28012          */
28013         "specialkey" : true
28014     });
28015 };
28016
28017 Roo.extend(Roo.Editor, Roo.Component, {
28018     /**
28019      * @cfg {Boolean/String} autosize
28020      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28021      * or "height" to adopt the height only (defaults to false)
28022      */
28023     /**
28024      * @cfg {Boolean} revertInvalid
28025      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28026      * validation fails (defaults to true)
28027      */
28028     /**
28029      * @cfg {Boolean} ignoreNoChange
28030      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28031      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28032      * will never be ignored.
28033      */
28034     /**
28035      * @cfg {Boolean} hideEl
28036      * False to keep the bound element visible while the editor is displayed (defaults to true)
28037      */
28038     /**
28039      * @cfg {Mixed} value
28040      * The data value of the underlying field (defaults to "")
28041      */
28042     value : "",
28043     /**
28044      * @cfg {String} alignment
28045      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28046      */
28047     alignment: "c-c?",
28048     /**
28049      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28050      * for bottom-right shadow (defaults to "frame")
28051      */
28052     shadow : "frame",
28053     /**
28054      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28055      */
28056     constrain : false,
28057     /**
28058      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28059      */
28060     completeOnEnter : false,
28061     /**
28062      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28063      */
28064     cancelOnEsc : false,
28065     /**
28066      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28067      */
28068     updateEl : false,
28069
28070     // private
28071     onRender : function(ct, position){
28072         this.el = new Roo.Layer({
28073             shadow: this.shadow,
28074             cls: "x-editor",
28075             parentEl : ct,
28076             shim : this.shim,
28077             shadowOffset:4,
28078             id: this.id,
28079             constrain: this.constrain
28080         });
28081         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28082         if(this.field.msgTarget != 'title'){
28083             this.field.msgTarget = 'qtip';
28084         }
28085         this.field.render(this.el);
28086         if(Roo.isGecko){
28087             this.field.el.dom.setAttribute('autocomplete', 'off');
28088         }
28089         this.field.on("specialkey", this.onSpecialKey, this);
28090         if(this.swallowKeys){
28091             this.field.el.swallowEvent(['keydown','keypress']);
28092         }
28093         this.field.show();
28094         this.field.on("blur", this.onBlur, this);
28095         if(this.field.grow){
28096             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28097         }
28098     },
28099
28100     onSpecialKey : function(field, e){
28101         if(this.completeOnEnter && e.getKey() == e.ENTER){
28102             e.stopEvent();
28103             this.completeEdit();
28104         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28105             this.cancelEdit();
28106         }else{
28107             this.fireEvent('specialkey', field, e);
28108         }
28109     },
28110
28111     /**
28112      * Starts the editing process and shows the editor.
28113      * @param {String/HTMLElement/Element} el The element to edit
28114      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28115       * to the innerHTML of el.
28116      */
28117     startEdit : function(el, value){
28118         if(this.editing){
28119             this.completeEdit();
28120         }
28121         this.boundEl = Roo.get(el);
28122         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28123         if(!this.rendered){
28124             this.render(this.parentEl || document.body);
28125         }
28126         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28127             return;
28128         }
28129         this.startValue = v;
28130         this.field.setValue(v);
28131         if(this.autoSize){
28132             var sz = this.boundEl.getSize();
28133             switch(this.autoSize){
28134                 case "width":
28135                 this.setSize(sz.width,  "");
28136                 break;
28137                 case "height":
28138                 this.setSize("",  sz.height);
28139                 break;
28140                 default:
28141                 this.setSize(sz.width,  sz.height);
28142             }
28143         }
28144         this.el.alignTo(this.boundEl, this.alignment);
28145         this.editing = true;
28146         if(Roo.QuickTips){
28147             Roo.QuickTips.disable();
28148         }
28149         this.show();
28150     },
28151
28152     /**
28153      * Sets the height and width of this editor.
28154      * @param {Number} width The new width
28155      * @param {Number} height The new height
28156      */
28157     setSize : function(w, h){
28158         this.field.setSize(w, h);
28159         if(this.el){
28160             this.el.sync();
28161         }
28162     },
28163
28164     /**
28165      * Realigns the editor to the bound field based on the current alignment config value.
28166      */
28167     realign : function(){
28168         this.el.alignTo(this.boundEl, this.alignment);
28169     },
28170
28171     /**
28172      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28173      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28174      */
28175     completeEdit : function(remainVisible){
28176         if(!this.editing){
28177             return;
28178         }
28179         var v = this.getValue();
28180         if(this.revertInvalid !== false && !this.field.isValid()){
28181             v = this.startValue;
28182             this.cancelEdit(true);
28183         }
28184         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28185             this.editing = false;
28186             this.hide();
28187             return;
28188         }
28189         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28190             this.editing = false;
28191             if(this.updateEl && this.boundEl){
28192                 this.boundEl.update(v);
28193             }
28194             if(remainVisible !== true){
28195                 this.hide();
28196             }
28197             this.fireEvent("complete", this, v, this.startValue);
28198         }
28199     },
28200
28201     // private
28202     onShow : function(){
28203         this.el.show();
28204         if(this.hideEl !== false){
28205             this.boundEl.hide();
28206         }
28207         this.field.show();
28208         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28209             this.fixIEFocus = true;
28210             this.deferredFocus.defer(50, this);
28211         }else{
28212             this.field.focus();
28213         }
28214         this.fireEvent("startedit", this.boundEl, this.startValue);
28215     },
28216
28217     deferredFocus : function(){
28218         if(this.editing){
28219             this.field.focus();
28220         }
28221     },
28222
28223     /**
28224      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28225      * reverted to the original starting value.
28226      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28227      * cancel (defaults to false)
28228      */
28229     cancelEdit : function(remainVisible){
28230         if(this.editing){
28231             this.setValue(this.startValue);
28232             if(remainVisible !== true){
28233                 this.hide();
28234             }
28235         }
28236     },
28237
28238     // private
28239     onBlur : function(){
28240         if(this.allowBlur !== true && this.editing){
28241             this.completeEdit();
28242         }
28243     },
28244
28245     // private
28246     onHide : function(){
28247         if(this.editing){
28248             this.completeEdit();
28249             return;
28250         }
28251         this.field.blur();
28252         if(this.field.collapse){
28253             this.field.collapse();
28254         }
28255         this.el.hide();
28256         if(this.hideEl !== false){
28257             this.boundEl.show();
28258         }
28259         if(Roo.QuickTips){
28260             Roo.QuickTips.enable();
28261         }
28262     },
28263
28264     /**
28265      * Sets the data value of the editor
28266      * @param {Mixed} value Any valid value supported by the underlying field
28267      */
28268     setValue : function(v){
28269         this.field.setValue(v);
28270     },
28271
28272     /**
28273      * Gets the data value of the editor
28274      * @return {Mixed} The data value
28275      */
28276     getValue : function(){
28277         return this.field.getValue();
28278     }
28279 });/*
28280  * Based on:
28281  * Ext JS Library 1.1.1
28282  * Copyright(c) 2006-2007, Ext JS, LLC.
28283  *
28284  * Originally Released Under LGPL - original licence link has changed is not relivant.
28285  *
28286  * Fork - LGPL
28287  * <script type="text/javascript">
28288  */
28289  
28290 /**
28291  * @class Roo.BasicDialog
28292  * @extends Roo.util.Observable
28293  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28294  * <pre><code>
28295 var dlg = new Roo.BasicDialog("my-dlg", {
28296     height: 200,
28297     width: 300,
28298     minHeight: 100,
28299     minWidth: 150,
28300     modal: true,
28301     proxyDrag: true,
28302     shadow: true
28303 });
28304 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28305 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28306 dlg.addButton('Cancel', dlg.hide, dlg);
28307 dlg.show();
28308 </code></pre>
28309   <b>A Dialog should always be a direct child of the body element.</b>
28310  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28311  * @cfg {String} title Default text to display in the title bar (defaults to null)
28312  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28313  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28314  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28315  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28316  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28317  * (defaults to null with no animation)
28318  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28319  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28320  * property for valid values (defaults to 'all')
28321  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28322  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28323  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28324  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28325  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28326  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28327  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28328  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28329  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28330  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28331  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28332  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28333  * draggable = true (defaults to false)
28334  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28335  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28336  * shadow (defaults to false)
28337  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28338  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28339  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28340  * @cfg {Array} buttons Array of buttons
28341  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28342  * @constructor
28343  * Create a new BasicDialog.
28344  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28345  * @param {Object} config Configuration options
28346  */
28347 Roo.BasicDialog = function(el, config){
28348     this.el = Roo.get(el);
28349     var dh = Roo.DomHelper;
28350     if(!this.el && config && config.autoCreate){
28351         if(typeof config.autoCreate == "object"){
28352             if(!config.autoCreate.id){
28353                 config.autoCreate.id = el;
28354             }
28355             this.el = dh.append(document.body,
28356                         config.autoCreate, true);
28357         }else{
28358             this.el = dh.append(document.body,
28359                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28360         }
28361     }
28362     el = this.el;
28363     el.setDisplayed(true);
28364     el.hide = this.hideAction;
28365     this.id = el.id;
28366     el.addClass("x-dlg");
28367
28368     Roo.apply(this, config);
28369
28370     this.proxy = el.createProxy("x-dlg-proxy");
28371     this.proxy.hide = this.hideAction;
28372     this.proxy.setOpacity(.5);
28373     this.proxy.hide();
28374
28375     if(config.width){
28376         el.setWidth(config.width);
28377     }
28378     if(config.height){
28379         el.setHeight(config.height);
28380     }
28381     this.size = el.getSize();
28382     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28383         this.xy = [config.x,config.y];
28384     }else{
28385         this.xy = el.getCenterXY(true);
28386     }
28387     /** The header element @type Roo.Element */
28388     this.header = el.child("> .x-dlg-hd");
28389     /** The body element @type Roo.Element */
28390     this.body = el.child("> .x-dlg-bd");
28391     /** The footer element @type Roo.Element */
28392     this.footer = el.child("> .x-dlg-ft");
28393
28394     if(!this.header){
28395         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28396     }
28397     if(!this.body){
28398         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28399     }
28400
28401     this.header.unselectable();
28402     if(this.title){
28403         this.header.update(this.title);
28404     }
28405     // this element allows the dialog to be focused for keyboard event
28406     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28407     this.focusEl.swallowEvent("click", true);
28408
28409     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28410
28411     // wrap the body and footer for special rendering
28412     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28413     if(this.footer){
28414         this.bwrap.dom.appendChild(this.footer.dom);
28415     }
28416
28417     this.bg = this.el.createChild({
28418         tag: "div", cls:"x-dlg-bg",
28419         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28420     });
28421     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28422
28423
28424     if(this.autoScroll !== false && !this.autoTabs){
28425         this.body.setStyle("overflow", "auto");
28426     }
28427
28428     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28429
28430     if(this.closable !== false){
28431         this.el.addClass("x-dlg-closable");
28432         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28433         this.close.on("click", this.closeClick, this);
28434         this.close.addClassOnOver("x-dlg-close-over");
28435     }
28436     if(this.collapsible !== false){
28437         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28438         this.collapseBtn.on("click", this.collapseClick, this);
28439         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28440         this.header.on("dblclick", this.collapseClick, this);
28441     }
28442     if(this.resizable !== false){
28443         this.el.addClass("x-dlg-resizable");
28444         this.resizer = new Roo.Resizable(el, {
28445             minWidth: this.minWidth || 80,
28446             minHeight:this.minHeight || 80,
28447             handles: this.resizeHandles || "all",
28448             pinned: true
28449         });
28450         this.resizer.on("beforeresize", this.beforeResize, this);
28451         this.resizer.on("resize", this.onResize, this);
28452     }
28453     if(this.draggable !== false){
28454         el.addClass("x-dlg-draggable");
28455         if (!this.proxyDrag) {
28456             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28457         }
28458         else {
28459             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28460         }
28461         dd.setHandleElId(this.header.id);
28462         dd.endDrag = this.endMove.createDelegate(this);
28463         dd.startDrag = this.startMove.createDelegate(this);
28464         dd.onDrag = this.onDrag.createDelegate(this);
28465         dd.scroll = false;
28466         this.dd = dd;
28467     }
28468     if(this.modal){
28469         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28470         this.mask.enableDisplayMode("block");
28471         this.mask.hide();
28472         this.el.addClass("x-dlg-modal");
28473     }
28474     if(this.shadow){
28475         this.shadow = new Roo.Shadow({
28476             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28477             offset : this.shadowOffset
28478         });
28479     }else{
28480         this.shadowOffset = 0;
28481     }
28482     if(Roo.useShims && this.shim !== false){
28483         this.shim = this.el.createShim();
28484         this.shim.hide = this.hideAction;
28485         this.shim.hide();
28486     }else{
28487         this.shim = false;
28488     }
28489     if(this.autoTabs){
28490         this.initTabs();
28491     }
28492     if (this.buttons) { 
28493         var bts= this.buttons;
28494         this.buttons = [];
28495         Roo.each(bts, function(b) {
28496             this.addButton(b);
28497         }, this);
28498     }
28499     
28500     
28501     this.addEvents({
28502         /**
28503          * @event keydown
28504          * Fires when a key is pressed
28505          * @param {Roo.BasicDialog} this
28506          * @param {Roo.EventObject} e
28507          */
28508         "keydown" : true,
28509         /**
28510          * @event move
28511          * Fires when this dialog is moved by the user.
28512          * @param {Roo.BasicDialog} this
28513          * @param {Number} x The new page X
28514          * @param {Number} y The new page Y
28515          */
28516         "move" : true,
28517         /**
28518          * @event resize
28519          * Fires when this dialog is resized by the user.
28520          * @param {Roo.BasicDialog} this
28521          * @param {Number} width The new width
28522          * @param {Number} height The new height
28523          */
28524         "resize" : true,
28525         /**
28526          * @event beforehide
28527          * Fires before this dialog is hidden.
28528          * @param {Roo.BasicDialog} this
28529          */
28530         "beforehide" : true,
28531         /**
28532          * @event hide
28533          * Fires when this dialog is hidden.
28534          * @param {Roo.BasicDialog} this
28535          */
28536         "hide" : true,
28537         /**
28538          * @event beforeshow
28539          * Fires before this dialog is shown.
28540          * @param {Roo.BasicDialog} this
28541          */
28542         "beforeshow" : true,
28543         /**
28544          * @event show
28545          * Fires when this dialog is shown.
28546          * @param {Roo.BasicDialog} this
28547          */
28548         "show" : true
28549     });
28550     el.on("keydown", this.onKeyDown, this);
28551     el.on("mousedown", this.toFront, this);
28552     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28553     this.el.hide();
28554     Roo.DialogManager.register(this);
28555     Roo.BasicDialog.superclass.constructor.call(this);
28556 };
28557
28558 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28559     shadowOffset: Roo.isIE ? 6 : 5,
28560     minHeight: 80,
28561     minWidth: 200,
28562     minButtonWidth: 75,
28563     defaultButton: null,
28564     buttonAlign: "right",
28565     tabTag: 'div',
28566     firstShow: true,
28567
28568     /**
28569      * Sets the dialog title text
28570      * @param {String} text The title text to display
28571      * @return {Roo.BasicDialog} this
28572      */
28573     setTitle : function(text){
28574         this.header.update(text);
28575         return this;
28576     },
28577
28578     // private
28579     closeClick : function(){
28580         this.hide();
28581     },
28582
28583     // private
28584     collapseClick : function(){
28585         this[this.collapsed ? "expand" : "collapse"]();
28586     },
28587
28588     /**
28589      * Collapses the dialog to its minimized state (only the title bar is visible).
28590      * Equivalent to the user clicking the collapse dialog button.
28591      */
28592     collapse : function(){
28593         if(!this.collapsed){
28594             this.collapsed = true;
28595             this.el.addClass("x-dlg-collapsed");
28596             this.restoreHeight = this.el.getHeight();
28597             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28598         }
28599     },
28600
28601     /**
28602      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28603      * clicking the expand dialog button.
28604      */
28605     expand : function(){
28606         if(this.collapsed){
28607             this.collapsed = false;
28608             this.el.removeClass("x-dlg-collapsed");
28609             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28610         }
28611     },
28612
28613     /**
28614      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28615      * @return {Roo.TabPanel} The tabs component
28616      */
28617     initTabs : function(){
28618         var tabs = this.getTabs();
28619         while(tabs.getTab(0)){
28620             tabs.removeTab(0);
28621         }
28622         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28623             var dom = el.dom;
28624             tabs.addTab(Roo.id(dom), dom.title);
28625             dom.title = "";
28626         });
28627         tabs.activate(0);
28628         return tabs;
28629     },
28630
28631     // private
28632     beforeResize : function(){
28633         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28634     },
28635
28636     // private
28637     onResize : function(){
28638         this.refreshSize();
28639         this.syncBodyHeight();
28640         this.adjustAssets();
28641         this.focus();
28642         this.fireEvent("resize", this, this.size.width, this.size.height);
28643     },
28644
28645     // private
28646     onKeyDown : function(e){
28647         if(this.isVisible()){
28648             this.fireEvent("keydown", this, e);
28649         }
28650     },
28651
28652     /**
28653      * Resizes the dialog.
28654      * @param {Number} width
28655      * @param {Number} height
28656      * @return {Roo.BasicDialog} this
28657      */
28658     resizeTo : function(width, height){
28659         this.el.setSize(width, height);
28660         this.size = {width: width, height: height};
28661         this.syncBodyHeight();
28662         if(this.fixedcenter){
28663             this.center();
28664         }
28665         if(this.isVisible()){
28666             this.constrainXY();
28667             this.adjustAssets();
28668         }
28669         this.fireEvent("resize", this, width, height);
28670         return this;
28671     },
28672
28673
28674     /**
28675      * Resizes the dialog to fit the specified content size.
28676      * @param {Number} width
28677      * @param {Number} height
28678      * @return {Roo.BasicDialog} this
28679      */
28680     setContentSize : function(w, h){
28681         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28682         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28683         //if(!this.el.isBorderBox()){
28684             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28685             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28686         //}
28687         if(this.tabs){
28688             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28689             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28690         }
28691         this.resizeTo(w, h);
28692         return this;
28693     },
28694
28695     /**
28696      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28697      * executed in response to a particular key being pressed while the dialog is active.
28698      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28699      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28700      * @param {Function} fn The function to call
28701      * @param {Object} scope (optional) The scope of the function
28702      * @return {Roo.BasicDialog} this
28703      */
28704     addKeyListener : function(key, fn, scope){
28705         var keyCode, shift, ctrl, alt;
28706         if(typeof key == "object" && !(key instanceof Array)){
28707             keyCode = key["key"];
28708             shift = key["shift"];
28709             ctrl = key["ctrl"];
28710             alt = key["alt"];
28711         }else{
28712             keyCode = key;
28713         }
28714         var handler = function(dlg, e){
28715             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28716                 var k = e.getKey();
28717                 if(keyCode instanceof Array){
28718                     for(var i = 0, len = keyCode.length; i < len; i++){
28719                         if(keyCode[i] == k){
28720                           fn.call(scope || window, dlg, k, e);
28721                           return;
28722                         }
28723                     }
28724                 }else{
28725                     if(k == keyCode){
28726                         fn.call(scope || window, dlg, k, e);
28727                     }
28728                 }
28729             }
28730         };
28731         this.on("keydown", handler);
28732         return this;
28733     },
28734
28735     /**
28736      * Returns the TabPanel component (creates it if it doesn't exist).
28737      * Note: If you wish to simply check for the existence of tabs without creating them,
28738      * check for a null 'tabs' property.
28739      * @return {Roo.TabPanel} The tabs component
28740      */
28741     getTabs : function(){
28742         if(!this.tabs){
28743             this.el.addClass("x-dlg-auto-tabs");
28744             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28745             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28746         }
28747         return this.tabs;
28748     },
28749
28750     /**
28751      * Adds a button to the footer section of the dialog.
28752      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28753      * object or a valid Roo.DomHelper element config
28754      * @param {Function} handler The function called when the button is clicked
28755      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28756      * @return {Roo.Button} The new button
28757      */
28758     addButton : function(config, handler, scope){
28759         var dh = Roo.DomHelper;
28760         if(!this.footer){
28761             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28762         }
28763         if(!this.btnContainer){
28764             var tb = this.footer.createChild({
28765
28766                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28767                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28768             }, null, true);
28769             this.btnContainer = tb.firstChild.firstChild.firstChild;
28770         }
28771         var bconfig = {
28772             handler: handler,
28773             scope: scope,
28774             minWidth: this.minButtonWidth,
28775             hideParent:true
28776         };
28777         if(typeof config == "string"){
28778             bconfig.text = config;
28779         }else{
28780             if(config.tag){
28781                 bconfig.dhconfig = config;
28782             }else{
28783                 Roo.apply(bconfig, config);
28784             }
28785         }
28786         var fc = false;
28787         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28788             bconfig.position = Math.max(0, bconfig.position);
28789             fc = this.btnContainer.childNodes[bconfig.position];
28790         }
28791          
28792         var btn = new Roo.Button(
28793             fc ? 
28794                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28795                 : this.btnContainer.appendChild(document.createElement("td")),
28796             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28797             bconfig
28798         );
28799         this.syncBodyHeight();
28800         if(!this.buttons){
28801             /**
28802              * Array of all the buttons that have been added to this dialog via addButton
28803              * @type Array
28804              */
28805             this.buttons = [];
28806         }
28807         this.buttons.push(btn);
28808         return btn;
28809     },
28810
28811     /**
28812      * Sets the default button to be focused when the dialog is displayed.
28813      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28814      * @return {Roo.BasicDialog} this
28815      */
28816     setDefaultButton : function(btn){
28817         this.defaultButton = btn;
28818         return this;
28819     },
28820
28821     // private
28822     getHeaderFooterHeight : function(safe){
28823         var height = 0;
28824         if(this.header){
28825            height += this.header.getHeight();
28826         }
28827         if(this.footer){
28828            var fm = this.footer.getMargins();
28829             height += (this.footer.getHeight()+fm.top+fm.bottom);
28830         }
28831         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28832         height += this.centerBg.getPadding("tb");
28833         return height;
28834     },
28835
28836     // private
28837     syncBodyHeight : function(){
28838         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28839         var height = this.size.height - this.getHeaderFooterHeight(false);
28840         bd.setHeight(height-bd.getMargins("tb"));
28841         var hh = this.header.getHeight();
28842         var h = this.size.height-hh;
28843         cb.setHeight(h);
28844         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28845         bw.setHeight(h-cb.getPadding("tb"));
28846         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28847         bd.setWidth(bw.getWidth(true));
28848         if(this.tabs){
28849             this.tabs.syncHeight();
28850             if(Roo.isIE){
28851                 this.tabs.el.repaint();
28852             }
28853         }
28854     },
28855
28856     /**
28857      * Restores the previous state of the dialog if Roo.state is configured.
28858      * @return {Roo.BasicDialog} this
28859      */
28860     restoreState : function(){
28861         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28862         if(box && box.width){
28863             this.xy = [box.x, box.y];
28864             this.resizeTo(box.width, box.height);
28865         }
28866         return this;
28867     },
28868
28869     // private
28870     beforeShow : function(){
28871         this.expand();
28872         if(this.fixedcenter){
28873             this.xy = this.el.getCenterXY(true);
28874         }
28875         if(this.modal){
28876             Roo.get(document.body).addClass("x-body-masked");
28877             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28878             this.mask.show();
28879         }
28880         this.constrainXY();
28881     },
28882
28883     // private
28884     animShow : function(){
28885         var b = Roo.get(this.animateTarget, true).getBox();
28886         this.proxy.setSize(b.width, b.height);
28887         this.proxy.setLocation(b.x, b.y);
28888         this.proxy.show();
28889         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28890                     true, .35, this.showEl.createDelegate(this));
28891     },
28892
28893     /**
28894      * Shows the dialog.
28895      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28896      * @return {Roo.BasicDialog} this
28897      */
28898     show : function(animateTarget){
28899         if (this.fireEvent("beforeshow", this) === false){
28900             return;
28901         }
28902         if(this.syncHeightBeforeShow){
28903             this.syncBodyHeight();
28904         }else if(this.firstShow){
28905             this.firstShow = false;
28906             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28907         }
28908         this.animateTarget = animateTarget || this.animateTarget;
28909         if(!this.el.isVisible()){
28910             this.beforeShow();
28911             if(this.animateTarget){
28912                 this.animShow();
28913             }else{
28914                 this.showEl();
28915             }
28916         }
28917         return this;
28918     },
28919
28920     // private
28921     showEl : function(){
28922         this.proxy.hide();
28923         this.el.setXY(this.xy);
28924         this.el.show();
28925         this.adjustAssets(true);
28926         this.toFront();
28927         this.focus();
28928         // IE peekaboo bug - fix found by Dave Fenwick
28929         if(Roo.isIE){
28930             this.el.repaint();
28931         }
28932         this.fireEvent("show", this);
28933     },
28934
28935     /**
28936      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28937      * dialog itself will receive focus.
28938      */
28939     focus : function(){
28940         if(this.defaultButton){
28941             this.defaultButton.focus();
28942         }else{
28943             this.focusEl.focus();
28944         }
28945     },
28946
28947     // private
28948     constrainXY : function(){
28949         if(this.constraintoviewport !== false){
28950             if(!this.viewSize){
28951                 if(this.container){
28952                     var s = this.container.getSize();
28953                     this.viewSize = [s.width, s.height];
28954                 }else{
28955                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28956                 }
28957             }
28958             var s = Roo.get(this.container||document).getScroll();
28959
28960             var x = this.xy[0], y = this.xy[1];
28961             var w = this.size.width, h = this.size.height;
28962             var vw = this.viewSize[0], vh = this.viewSize[1];
28963             // only move it if it needs it
28964             var moved = false;
28965             // first validate right/bottom
28966             if(x + w > vw+s.left){
28967                 x = vw - w;
28968                 moved = true;
28969             }
28970             if(y + h > vh+s.top){
28971                 y = vh - h;
28972                 moved = true;
28973             }
28974             // then make sure top/left isn't negative
28975             if(x < s.left){
28976                 x = s.left;
28977                 moved = true;
28978             }
28979             if(y < s.top){
28980                 y = s.top;
28981                 moved = true;
28982             }
28983             if(moved){
28984                 // cache xy
28985                 this.xy = [x, y];
28986                 if(this.isVisible()){
28987                     this.el.setLocation(x, y);
28988                     this.adjustAssets();
28989                 }
28990             }
28991         }
28992     },
28993
28994     // private
28995     onDrag : function(){
28996         if(!this.proxyDrag){
28997             this.xy = this.el.getXY();
28998             this.adjustAssets();
28999         }
29000     },
29001
29002     // private
29003     adjustAssets : function(doShow){
29004         var x = this.xy[0], y = this.xy[1];
29005         var w = this.size.width, h = this.size.height;
29006         if(doShow === true){
29007             if(this.shadow){
29008                 this.shadow.show(this.el);
29009             }
29010             if(this.shim){
29011                 this.shim.show();
29012             }
29013         }
29014         if(this.shadow && this.shadow.isVisible()){
29015             this.shadow.show(this.el);
29016         }
29017         if(this.shim && this.shim.isVisible()){
29018             this.shim.setBounds(x, y, w, h);
29019         }
29020     },
29021
29022     // private
29023     adjustViewport : function(w, h){
29024         if(!w || !h){
29025             w = Roo.lib.Dom.getViewWidth();
29026             h = Roo.lib.Dom.getViewHeight();
29027         }
29028         // cache the size
29029         this.viewSize = [w, h];
29030         if(this.modal && this.mask.isVisible()){
29031             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29032             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29033         }
29034         if(this.isVisible()){
29035             this.constrainXY();
29036         }
29037     },
29038
29039     /**
29040      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29041      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29042      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29043      */
29044     destroy : function(removeEl){
29045         if(this.isVisible()){
29046             this.animateTarget = null;
29047             this.hide();
29048         }
29049         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29050         if(this.tabs){
29051             this.tabs.destroy(removeEl);
29052         }
29053         Roo.destroy(
29054              this.shim,
29055              this.proxy,
29056              this.resizer,
29057              this.close,
29058              this.mask
29059         );
29060         if(this.dd){
29061             this.dd.unreg();
29062         }
29063         if(this.buttons){
29064            for(var i = 0, len = this.buttons.length; i < len; i++){
29065                this.buttons[i].destroy();
29066            }
29067         }
29068         this.el.removeAllListeners();
29069         if(removeEl === true){
29070             this.el.update("");
29071             this.el.remove();
29072         }
29073         Roo.DialogManager.unregister(this);
29074     },
29075
29076     // private
29077     startMove : function(){
29078         if(this.proxyDrag){
29079             this.proxy.show();
29080         }
29081         if(this.constraintoviewport !== false){
29082             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29083         }
29084     },
29085
29086     // private
29087     endMove : function(){
29088         if(!this.proxyDrag){
29089             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29090         }else{
29091             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29092             this.proxy.hide();
29093         }
29094         this.refreshSize();
29095         this.adjustAssets();
29096         this.focus();
29097         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29098     },
29099
29100     /**
29101      * Brings this dialog to the front of any other visible dialogs
29102      * @return {Roo.BasicDialog} this
29103      */
29104     toFront : function(){
29105         Roo.DialogManager.bringToFront(this);
29106         return this;
29107     },
29108
29109     /**
29110      * Sends this dialog to the back (under) of any other visible dialogs
29111      * @return {Roo.BasicDialog} this
29112      */
29113     toBack : function(){
29114         Roo.DialogManager.sendToBack(this);
29115         return this;
29116     },
29117
29118     /**
29119      * Centers this dialog in the viewport
29120      * @return {Roo.BasicDialog} this
29121      */
29122     center : function(){
29123         var xy = this.el.getCenterXY(true);
29124         this.moveTo(xy[0], xy[1]);
29125         return this;
29126     },
29127
29128     /**
29129      * Moves the dialog's top-left corner to the specified point
29130      * @param {Number} x
29131      * @param {Number} y
29132      * @return {Roo.BasicDialog} this
29133      */
29134     moveTo : function(x, y){
29135         this.xy = [x,y];
29136         if(this.isVisible()){
29137             this.el.setXY(this.xy);
29138             this.adjustAssets();
29139         }
29140         return this;
29141     },
29142
29143     /**
29144      * Aligns the dialog to the specified element
29145      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29146      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29147      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29148      * @return {Roo.BasicDialog} this
29149      */
29150     alignTo : function(element, position, offsets){
29151         this.xy = this.el.getAlignToXY(element, position, offsets);
29152         if(this.isVisible()){
29153             this.el.setXY(this.xy);
29154             this.adjustAssets();
29155         }
29156         return this;
29157     },
29158
29159     /**
29160      * Anchors an element to another element and realigns it when the window is resized.
29161      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29162      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29163      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29164      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29165      * is a number, it is used as the buffer delay (defaults to 50ms).
29166      * @return {Roo.BasicDialog} this
29167      */
29168     anchorTo : function(el, alignment, offsets, monitorScroll){
29169         var action = function(){
29170             this.alignTo(el, alignment, offsets);
29171         };
29172         Roo.EventManager.onWindowResize(action, this);
29173         var tm = typeof monitorScroll;
29174         if(tm != 'undefined'){
29175             Roo.EventManager.on(window, 'scroll', action, this,
29176                 {buffer: tm == 'number' ? monitorScroll : 50});
29177         }
29178         action.call(this);
29179         return this;
29180     },
29181
29182     /**
29183      * Returns true if the dialog is visible
29184      * @return {Boolean}
29185      */
29186     isVisible : function(){
29187         return this.el.isVisible();
29188     },
29189
29190     // private
29191     animHide : function(callback){
29192         var b = Roo.get(this.animateTarget).getBox();
29193         this.proxy.show();
29194         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29195         this.el.hide();
29196         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29197                     this.hideEl.createDelegate(this, [callback]));
29198     },
29199
29200     /**
29201      * Hides the dialog.
29202      * @param {Function} callback (optional) Function to call when the dialog is hidden
29203      * @return {Roo.BasicDialog} this
29204      */
29205     hide : function(callback){
29206         if (this.fireEvent("beforehide", this) === false){
29207             return;
29208         }
29209         if(this.shadow){
29210             this.shadow.hide();
29211         }
29212         if(this.shim) {
29213           this.shim.hide();
29214         }
29215         if(this.animateTarget){
29216            this.animHide(callback);
29217         }else{
29218             this.el.hide();
29219             this.hideEl(callback);
29220         }
29221         return this;
29222     },
29223
29224     // private
29225     hideEl : function(callback){
29226         this.proxy.hide();
29227         if(this.modal){
29228             this.mask.hide();
29229             Roo.get(document.body).removeClass("x-body-masked");
29230         }
29231         this.fireEvent("hide", this);
29232         if(typeof callback == "function"){
29233             callback();
29234         }
29235     },
29236
29237     // private
29238     hideAction : function(){
29239         this.setLeft("-10000px");
29240         this.setTop("-10000px");
29241         this.setStyle("visibility", "hidden");
29242     },
29243
29244     // private
29245     refreshSize : function(){
29246         this.size = this.el.getSize();
29247         this.xy = this.el.getXY();
29248         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29249     },
29250
29251     // private
29252     // z-index is managed by the DialogManager and may be overwritten at any time
29253     setZIndex : function(index){
29254         if(this.modal){
29255             this.mask.setStyle("z-index", index);
29256         }
29257         if(this.shim){
29258             this.shim.setStyle("z-index", ++index);
29259         }
29260         if(this.shadow){
29261             this.shadow.setZIndex(++index);
29262         }
29263         this.el.setStyle("z-index", ++index);
29264         if(this.proxy){
29265             this.proxy.setStyle("z-index", ++index);
29266         }
29267         if(this.resizer){
29268             this.resizer.proxy.setStyle("z-index", ++index);
29269         }
29270
29271         this.lastZIndex = index;
29272     },
29273
29274     /**
29275      * Returns the element for this dialog
29276      * @return {Roo.Element} The underlying dialog Element
29277      */
29278     getEl : function(){
29279         return this.el;
29280     }
29281 });
29282
29283 /**
29284  * @class Roo.DialogManager
29285  * Provides global access to BasicDialogs that have been created and
29286  * support for z-indexing (layering) multiple open dialogs.
29287  */
29288 Roo.DialogManager = function(){
29289     var list = {};
29290     var accessList = [];
29291     var front = null;
29292
29293     // private
29294     var sortDialogs = function(d1, d2){
29295         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29296     };
29297
29298     // private
29299     var orderDialogs = function(){
29300         accessList.sort(sortDialogs);
29301         var seed = Roo.DialogManager.zseed;
29302         for(var i = 0, len = accessList.length; i < len; i++){
29303             var dlg = accessList[i];
29304             if(dlg){
29305                 dlg.setZIndex(seed + (i*10));
29306             }
29307         }
29308     };
29309
29310     return {
29311         /**
29312          * The starting z-index for BasicDialogs (defaults to 9000)
29313          * @type Number The z-index value
29314          */
29315         zseed : 9000,
29316
29317         // private
29318         register : function(dlg){
29319             list[dlg.id] = dlg;
29320             accessList.push(dlg);
29321         },
29322
29323         // private
29324         unregister : function(dlg){
29325             delete list[dlg.id];
29326             var i=0;
29327             var len=0;
29328             if(!accessList.indexOf){
29329                 for(  i = 0, len = accessList.length; i < len; i++){
29330                     if(accessList[i] == dlg){
29331                         accessList.splice(i, 1);
29332                         return;
29333                     }
29334                 }
29335             }else{
29336                  i = accessList.indexOf(dlg);
29337                 if(i != -1){
29338                     accessList.splice(i, 1);
29339                 }
29340             }
29341         },
29342
29343         /**
29344          * Gets a registered dialog by id
29345          * @param {String/Object} id The id of the dialog or a dialog
29346          * @return {Roo.BasicDialog} this
29347          */
29348         get : function(id){
29349             return typeof id == "object" ? id : list[id];
29350         },
29351
29352         /**
29353          * Brings the specified dialog to the front
29354          * @param {String/Object} dlg The id of the dialog or a dialog
29355          * @return {Roo.BasicDialog} this
29356          */
29357         bringToFront : function(dlg){
29358             dlg = this.get(dlg);
29359             if(dlg != front){
29360                 front = dlg;
29361                 dlg._lastAccess = new Date().getTime();
29362                 orderDialogs();
29363             }
29364             return dlg;
29365         },
29366
29367         /**
29368          * Sends the specified dialog to the back
29369          * @param {String/Object} dlg The id of the dialog or a dialog
29370          * @return {Roo.BasicDialog} this
29371          */
29372         sendToBack : function(dlg){
29373             dlg = this.get(dlg);
29374             dlg._lastAccess = -(new Date().getTime());
29375             orderDialogs();
29376             return dlg;
29377         },
29378
29379         /**
29380          * Hides all dialogs
29381          */
29382         hideAll : function(){
29383             for(var id in list){
29384                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29385                     list[id].hide();
29386                 }
29387             }
29388         }
29389     };
29390 }();
29391
29392 /**
29393  * @class Roo.LayoutDialog
29394  * @extends Roo.BasicDialog
29395  * Dialog which provides adjustments for working with a layout in a Dialog.
29396  * Add your necessary layout config options to the dialog's config.<br>
29397  * Example usage (including a nested layout):
29398  * <pre><code>
29399 if(!dialog){
29400     dialog = new Roo.LayoutDialog("download-dlg", {
29401         modal: true,
29402         width:600,
29403         height:450,
29404         shadow:true,
29405         minWidth:500,
29406         minHeight:350,
29407         autoTabs:true,
29408         proxyDrag:true,
29409         // layout config merges with the dialog config
29410         center:{
29411             tabPosition: "top",
29412             alwaysShowTabs: true
29413         }
29414     });
29415     dialog.addKeyListener(27, dialog.hide, dialog);
29416     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29417     dialog.addButton("Build It!", this.getDownload, this);
29418
29419     // we can even add nested layouts
29420     var innerLayout = new Roo.BorderLayout("dl-inner", {
29421         east: {
29422             initialSize: 200,
29423             autoScroll:true,
29424             split:true
29425         },
29426         center: {
29427             autoScroll:true
29428         }
29429     });
29430     innerLayout.beginUpdate();
29431     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29432     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29433     innerLayout.endUpdate(true);
29434
29435     var layout = dialog.getLayout();
29436     layout.beginUpdate();
29437     layout.add("center", new Roo.ContentPanel("standard-panel",
29438                         {title: "Download the Source", fitToFrame:true}));
29439     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29440                {title: "Build your own roo.js"}));
29441     layout.getRegion("center").showPanel(sp);
29442     layout.endUpdate();
29443 }
29444 </code></pre>
29445     * @constructor
29446     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29447     * @param {Object} config configuration options
29448   */
29449 Roo.LayoutDialog = function(el, cfg){
29450     
29451     var config=  cfg;
29452     if (typeof(cfg) == 'undefined') {
29453         config = Roo.apply({}, el);
29454         el = Roo.get( document.documentElement || document.body).createChild();
29455         //config.autoCreate = true;
29456     }
29457     
29458     
29459     config.autoTabs = false;
29460     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29461     this.body.setStyle({overflow:"hidden", position:"relative"});
29462     this.layout = new Roo.BorderLayout(this.body.dom, config);
29463     this.layout.monitorWindowResize = false;
29464     this.el.addClass("x-dlg-auto-layout");
29465     // fix case when center region overwrites center function
29466     this.center = Roo.BasicDialog.prototype.center;
29467     this.on("show", this.layout.layout, this.layout, true);
29468     if (config.items) {
29469         var xitems = config.items;
29470         delete config.items;
29471         Roo.each(xitems, this.addxtype, this);
29472     }
29473     
29474     
29475 };
29476 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29477     /**
29478      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29479      * @deprecated
29480      */
29481     endUpdate : function(){
29482         this.layout.endUpdate();
29483     },
29484
29485     /**
29486      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29487      *  @deprecated
29488      */
29489     beginUpdate : function(){
29490         this.layout.beginUpdate();
29491     },
29492
29493     /**
29494      * Get the BorderLayout for this dialog
29495      * @return {Roo.BorderLayout}
29496      */
29497     getLayout : function(){
29498         return this.layout;
29499     },
29500
29501     showEl : function(){
29502         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29503         if(Roo.isIE7){
29504             this.layout.layout();
29505         }
29506     },
29507
29508     // private
29509     // Use the syncHeightBeforeShow config option to control this automatically
29510     syncBodyHeight : function(){
29511         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29512         if(this.layout){this.layout.layout();}
29513     },
29514     
29515       /**
29516      * Add an xtype element (actually adds to the layout.)
29517      * @return {Object} xdata xtype object data.
29518      */
29519     
29520     addxtype : function(c) {
29521         return this.layout.addxtype(c);
29522     }
29523 });/*
29524  * Based on:
29525  * Ext JS Library 1.1.1
29526  * Copyright(c) 2006-2007, Ext JS, LLC.
29527  *
29528  * Originally Released Under LGPL - original licence link has changed is not relivant.
29529  *
29530  * Fork - LGPL
29531  * <script type="text/javascript">
29532  */
29533  
29534 /**
29535  * @class Roo.MessageBox
29536  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29537  * Example usage:
29538  *<pre><code>
29539 // Basic alert:
29540 Roo.Msg.alert('Status', 'Changes saved successfully.');
29541
29542 // Prompt for user data:
29543 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29544     if (btn == 'ok'){
29545         // process text value...
29546     }
29547 });
29548
29549 // Show a dialog using config options:
29550 Roo.Msg.show({
29551    title:'Save Changes?',
29552    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29553    buttons: Roo.Msg.YESNOCANCEL,
29554    fn: processResult,
29555    animEl: 'elId'
29556 });
29557 </code></pre>
29558  * @singleton
29559  */
29560 Roo.MessageBox = function(){
29561     var dlg, opt, mask, waitTimer;
29562     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29563     var buttons, activeTextEl, bwidth;
29564
29565     // private
29566     var handleButton = function(button){
29567         dlg.hide();
29568         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29569     };
29570
29571     // private
29572     var handleHide = function(){
29573         if(opt && opt.cls){
29574             dlg.el.removeClass(opt.cls);
29575         }
29576         if(waitTimer){
29577             Roo.TaskMgr.stop(waitTimer);
29578             waitTimer = null;
29579         }
29580     };
29581
29582     // private
29583     var updateButtons = function(b){
29584         var width = 0;
29585         if(!b){
29586             buttons["ok"].hide();
29587             buttons["cancel"].hide();
29588             buttons["yes"].hide();
29589             buttons["no"].hide();
29590             dlg.footer.dom.style.display = 'none';
29591             return width;
29592         }
29593         dlg.footer.dom.style.display = '';
29594         for(var k in buttons){
29595             if(typeof buttons[k] != "function"){
29596                 if(b[k]){
29597                     buttons[k].show();
29598                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29599                     width += buttons[k].el.getWidth()+15;
29600                 }else{
29601                     buttons[k].hide();
29602                 }
29603             }
29604         }
29605         return width;
29606     };
29607
29608     // private
29609     var handleEsc = function(d, k, e){
29610         if(opt && opt.closable !== false){
29611             dlg.hide();
29612         }
29613         if(e){
29614             e.stopEvent();
29615         }
29616     };
29617
29618     return {
29619         /**
29620          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29621          * @return {Roo.BasicDialog} The BasicDialog element
29622          */
29623         getDialog : function(){
29624            if(!dlg){
29625                 dlg = new Roo.BasicDialog("x-msg-box", {
29626                     autoCreate : true,
29627                     shadow: true,
29628                     draggable: true,
29629                     resizable:false,
29630                     constraintoviewport:false,
29631                     fixedcenter:true,
29632                     collapsible : false,
29633                     shim:true,
29634                     modal: true,
29635                     width:400, height:100,
29636                     buttonAlign:"center",
29637                     closeClick : function(){
29638                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29639                             handleButton("no");
29640                         }else{
29641                             handleButton("cancel");
29642                         }
29643                     }
29644                 });
29645                 dlg.on("hide", handleHide);
29646                 mask = dlg.mask;
29647                 dlg.addKeyListener(27, handleEsc);
29648                 buttons = {};
29649                 var bt = this.buttonText;
29650                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29651                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29652                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29653                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29654                 bodyEl = dlg.body.createChild({
29655
29656                     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>'
29657                 });
29658                 msgEl = bodyEl.dom.firstChild;
29659                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29660                 textboxEl.enableDisplayMode();
29661                 textboxEl.addKeyListener([10,13], function(){
29662                     if(dlg.isVisible() && opt && opt.buttons){
29663                         if(opt.buttons.ok){
29664                             handleButton("ok");
29665                         }else if(opt.buttons.yes){
29666                             handleButton("yes");
29667                         }
29668                     }
29669                 });
29670                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29671                 textareaEl.enableDisplayMode();
29672                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29673                 progressEl.enableDisplayMode();
29674                 var pf = progressEl.dom.firstChild;
29675                 if (pf) {
29676                     pp = Roo.get(pf.firstChild);
29677                     pp.setHeight(pf.offsetHeight);
29678                 }
29679                 
29680             }
29681             return dlg;
29682         },
29683
29684         /**
29685          * Updates the message box body text
29686          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29687          * the XHTML-compliant non-breaking space character '&amp;#160;')
29688          * @return {Roo.MessageBox} This message box
29689          */
29690         updateText : function(text){
29691             if(!dlg.isVisible() && !opt.width){
29692                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29693             }
29694             msgEl.innerHTML = text || '&#160;';
29695             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29696                         Math.max(opt.minWidth || this.minWidth, bwidth));
29697             if(opt.prompt){
29698                 activeTextEl.setWidth(w);
29699             }
29700             if(dlg.isVisible()){
29701                 dlg.fixedcenter = false;
29702             }
29703             dlg.setContentSize(w, bodyEl.getHeight());
29704             if(dlg.isVisible()){
29705                 dlg.fixedcenter = true;
29706             }
29707             return this;
29708         },
29709
29710         /**
29711          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29712          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29713          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29714          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29715          * @return {Roo.MessageBox} This message box
29716          */
29717         updateProgress : function(value, text){
29718             if(text){
29719                 this.updateText(text);
29720             }
29721             if (pp) { // weird bug on my firefox - for some reason this is not defined
29722                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29723             }
29724             return this;
29725         },        
29726
29727         /**
29728          * Returns true if the message box is currently displayed
29729          * @return {Boolean} True if the message box is visible, else false
29730          */
29731         isVisible : function(){
29732             return dlg && dlg.isVisible();  
29733         },
29734
29735         /**
29736          * Hides the message box if it is displayed
29737          */
29738         hide : function(){
29739             if(this.isVisible()){
29740                 dlg.hide();
29741             }  
29742         },
29743
29744         /**
29745          * Displays a new message box, or reinitializes an existing message box, based on the config options
29746          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29747          * The following config object properties are supported:
29748          * <pre>
29749 Property    Type             Description
29750 ----------  ---------------  ------------------------------------------------------------------------------------
29751 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29752                                    closes (defaults to undefined)
29753 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29754                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29755 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29756                                    progress and wait dialogs will ignore this property and always hide the
29757                                    close button as they can only be closed programmatically.
29758 cls               String           A custom CSS class to apply to the message box element
29759 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29760                                    displayed (defaults to 75)
29761 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29762                                    function will be btn (the name of the button that was clicked, if applicable,
29763                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29764                                    Progress and wait dialogs will ignore this option since they do not respond to
29765                                    user actions and can only be closed programmatically, so any required function
29766                                    should be called by the same code after it closes the dialog.
29767 icon              String           A CSS class that provides a background image to be used as an icon for
29768                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29769 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29770 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29771 modal             Boolean          False to allow user interaction with the page while the message box is
29772                                    displayed (defaults to true)
29773 msg               String           A string that will replace the existing message box body text (defaults
29774                                    to the XHTML-compliant non-breaking space character '&#160;')
29775 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29776 progress          Boolean          True to display a progress bar (defaults to false)
29777 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29778 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29779 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29780 title             String           The title text
29781 value             String           The string value to set into the active textbox element if displayed
29782 wait              Boolean          True to display a progress bar (defaults to false)
29783 width             Number           The width of the dialog in pixels
29784 </pre>
29785          *
29786          * Example usage:
29787          * <pre><code>
29788 Roo.Msg.show({
29789    title: 'Address',
29790    msg: 'Please enter your address:',
29791    width: 300,
29792    buttons: Roo.MessageBox.OKCANCEL,
29793    multiline: true,
29794    fn: saveAddress,
29795    animEl: 'addAddressBtn'
29796 });
29797 </code></pre>
29798          * @param {Object} config Configuration options
29799          * @return {Roo.MessageBox} This message box
29800          */
29801         show : function(options){
29802             if(this.isVisible()){
29803                 this.hide();
29804             }
29805             var d = this.getDialog();
29806             opt = options;
29807             d.setTitle(opt.title || "&#160;");
29808             d.close.setDisplayed(opt.closable !== false);
29809             activeTextEl = textboxEl;
29810             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29811             if(opt.prompt){
29812                 if(opt.multiline){
29813                     textboxEl.hide();
29814                     textareaEl.show();
29815                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29816                         opt.multiline : this.defaultTextHeight);
29817                     activeTextEl = textareaEl;
29818                 }else{
29819                     textboxEl.show();
29820                     textareaEl.hide();
29821                 }
29822             }else{
29823                 textboxEl.hide();
29824                 textareaEl.hide();
29825             }
29826             progressEl.setDisplayed(opt.progress === true);
29827             this.updateProgress(0);
29828             activeTextEl.dom.value = opt.value || "";
29829             if(opt.prompt){
29830                 dlg.setDefaultButton(activeTextEl);
29831             }else{
29832                 var bs = opt.buttons;
29833                 var db = null;
29834                 if(bs && bs.ok){
29835                     db = buttons["ok"];
29836                 }else if(bs && bs.yes){
29837                     db = buttons["yes"];
29838                 }
29839                 dlg.setDefaultButton(db);
29840             }
29841             bwidth = updateButtons(opt.buttons);
29842             this.updateText(opt.msg);
29843             if(opt.cls){
29844                 d.el.addClass(opt.cls);
29845             }
29846             d.proxyDrag = opt.proxyDrag === true;
29847             d.modal = opt.modal !== false;
29848             d.mask = opt.modal !== false ? mask : false;
29849             if(!d.isVisible()){
29850                 // force it to the end of the z-index stack so it gets a cursor in FF
29851                 document.body.appendChild(dlg.el.dom);
29852                 d.animateTarget = null;
29853                 d.show(options.animEl);
29854             }
29855             return this;
29856         },
29857
29858         /**
29859          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29860          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29861          * and closing the message box when the process is complete.
29862          * @param {String} title The title bar text
29863          * @param {String} msg The message box body text
29864          * @return {Roo.MessageBox} This message box
29865          */
29866         progress : function(title, msg){
29867             this.show({
29868                 title : title,
29869                 msg : msg,
29870                 buttons: false,
29871                 progress:true,
29872                 closable:false,
29873                 minWidth: this.minProgressWidth,
29874                 modal : true
29875             });
29876             return this;
29877         },
29878
29879         /**
29880          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29881          * If a callback function is passed it will be called after the user clicks the button, and the
29882          * id of the button that was clicked will be passed as the only parameter to the callback
29883          * (could also be the top-right close button).
29884          * @param {String} title The title bar text
29885          * @param {String} msg The message box body text
29886          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29887          * @param {Object} scope (optional) The scope of the callback function
29888          * @return {Roo.MessageBox} This message box
29889          */
29890         alert : function(title, msg, fn, scope){
29891             this.show({
29892                 title : title,
29893                 msg : msg,
29894                 buttons: this.OK,
29895                 fn: fn,
29896                 scope : scope,
29897                 modal : true
29898             });
29899             return this;
29900         },
29901
29902         /**
29903          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29904          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29905          * You are responsible for closing the message box when the process is complete.
29906          * @param {String} msg The message box body text
29907          * @param {String} title (optional) The title bar text
29908          * @return {Roo.MessageBox} This message box
29909          */
29910         wait : function(msg, title){
29911             this.show({
29912                 title : title,
29913                 msg : msg,
29914                 buttons: false,
29915                 closable:false,
29916                 progress:true,
29917                 modal:true,
29918                 width:300,
29919                 wait:true
29920             });
29921             waitTimer = Roo.TaskMgr.start({
29922                 run: function(i){
29923                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29924                 },
29925                 interval: 1000
29926             });
29927             return this;
29928         },
29929
29930         /**
29931          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29932          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29933          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29934          * @param {String} title The title bar text
29935          * @param {String} msg The message box body text
29936          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29937          * @param {Object} scope (optional) The scope of the callback function
29938          * @return {Roo.MessageBox} This message box
29939          */
29940         confirm : function(title, msg, fn, scope){
29941             this.show({
29942                 title : title,
29943                 msg : msg,
29944                 buttons: this.YESNO,
29945                 fn: fn,
29946                 scope : scope,
29947                 modal : true
29948             });
29949             return this;
29950         },
29951
29952         /**
29953          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29954          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29955          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29956          * (could also be the top-right close button) and the text that was entered will be passed as the two
29957          * parameters to the callback.
29958          * @param {String} title The title bar text
29959          * @param {String} msg The message box body text
29960          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29961          * @param {Object} scope (optional) The scope of the callback function
29962          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29963          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29964          * @return {Roo.MessageBox} This message box
29965          */
29966         prompt : function(title, msg, fn, scope, multiline){
29967             this.show({
29968                 title : title,
29969                 msg : msg,
29970                 buttons: this.OKCANCEL,
29971                 fn: fn,
29972                 minWidth:250,
29973                 scope : scope,
29974                 prompt:true,
29975                 multiline: multiline,
29976                 modal : true
29977             });
29978             return this;
29979         },
29980
29981         /**
29982          * Button config that displays a single OK button
29983          * @type Object
29984          */
29985         OK : {ok:true},
29986         /**
29987          * Button config that displays Yes and No buttons
29988          * @type Object
29989          */
29990         YESNO : {yes:true, no:true},
29991         /**
29992          * Button config that displays OK and Cancel buttons
29993          * @type Object
29994          */
29995         OKCANCEL : {ok:true, cancel:true},
29996         /**
29997          * Button config that displays Yes, No and Cancel buttons
29998          * @type Object
29999          */
30000         YESNOCANCEL : {yes:true, no:true, cancel:true},
30001
30002         /**
30003          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30004          * @type Number
30005          */
30006         defaultTextHeight : 75,
30007         /**
30008          * The maximum width in pixels of the message box (defaults to 600)
30009          * @type Number
30010          */
30011         maxWidth : 600,
30012         /**
30013          * The minimum width in pixels of the message box (defaults to 100)
30014          * @type Number
30015          */
30016         minWidth : 100,
30017         /**
30018          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30019          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30020          * @type Number
30021          */
30022         minProgressWidth : 250,
30023         /**
30024          * An object containing the default button text strings that can be overriden for localized language support.
30025          * Supported properties are: ok, cancel, yes and no.
30026          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30027          * @type Object
30028          */
30029         buttonText : {
30030             ok : "OK",
30031             cancel : "Cancel",
30032             yes : "Yes",
30033             no : "No"
30034         }
30035     };
30036 }();
30037
30038 /**
30039  * Shorthand for {@link Roo.MessageBox}
30040  */
30041 Roo.Msg = Roo.MessageBox;/*
30042  * Based on:
30043  * Ext JS Library 1.1.1
30044  * Copyright(c) 2006-2007, Ext JS, LLC.
30045  *
30046  * Originally Released Under LGPL - original licence link has changed is not relivant.
30047  *
30048  * Fork - LGPL
30049  * <script type="text/javascript">
30050  */
30051 /**
30052  * @class Roo.QuickTips
30053  * Provides attractive and customizable tooltips for any element.
30054  * @singleton
30055  */
30056 Roo.QuickTips = function(){
30057     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30058     var ce, bd, xy, dd;
30059     var visible = false, disabled = true, inited = false;
30060     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30061     
30062     var onOver = function(e){
30063         if(disabled){
30064             return;
30065         }
30066         var t = e.getTarget();
30067         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30068             return;
30069         }
30070         if(ce && t == ce.el){
30071             clearTimeout(hideProc);
30072             return;
30073         }
30074         if(t && tagEls[t.id]){
30075             tagEls[t.id].el = t;
30076             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30077             return;
30078         }
30079         var ttp, et = Roo.fly(t);
30080         var ns = cfg.namespace;
30081         if(tm.interceptTitles && t.title){
30082             ttp = t.title;
30083             t.qtip = ttp;
30084             t.removeAttribute("title");
30085             e.preventDefault();
30086         }else{
30087             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30088         }
30089         if(ttp){
30090             showProc = show.defer(tm.showDelay, tm, [{
30091                 el: t, 
30092                 text: ttp, 
30093                 width: et.getAttributeNS(ns, cfg.width),
30094                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30095                 title: et.getAttributeNS(ns, cfg.title),
30096                     cls: et.getAttributeNS(ns, cfg.cls)
30097             }]);
30098         }
30099     };
30100     
30101     var onOut = function(e){
30102         clearTimeout(showProc);
30103         var t = e.getTarget();
30104         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30105             hideProc = setTimeout(hide, tm.hideDelay);
30106         }
30107     };
30108     
30109     var onMove = function(e){
30110         if(disabled){
30111             return;
30112         }
30113         xy = e.getXY();
30114         xy[1] += 18;
30115         if(tm.trackMouse && ce){
30116             el.setXY(xy);
30117         }
30118     };
30119     
30120     var onDown = function(e){
30121         clearTimeout(showProc);
30122         clearTimeout(hideProc);
30123         if(!e.within(el)){
30124             if(tm.hideOnClick){
30125                 hide();
30126                 tm.disable();
30127                 tm.enable.defer(100, tm);
30128             }
30129         }
30130     };
30131     
30132     var getPad = function(){
30133         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30134     };
30135
30136     var show = function(o){
30137         if(disabled){
30138             return;
30139         }
30140         clearTimeout(dismissProc);
30141         ce = o;
30142         if(removeCls){ // in case manually hidden
30143             el.removeClass(removeCls);
30144             removeCls = null;
30145         }
30146         if(ce.cls){
30147             el.addClass(ce.cls);
30148             removeCls = ce.cls;
30149         }
30150         if(ce.title){
30151             tipTitle.update(ce.title);
30152             tipTitle.show();
30153         }else{
30154             tipTitle.update('');
30155             tipTitle.hide();
30156         }
30157         el.dom.style.width  = tm.maxWidth+'px';
30158         //tipBody.dom.style.width = '';
30159         tipBodyText.update(o.text);
30160         var p = getPad(), w = ce.width;
30161         if(!w){
30162             var td = tipBodyText.dom;
30163             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30164             if(aw > tm.maxWidth){
30165                 w = tm.maxWidth;
30166             }else if(aw < tm.minWidth){
30167                 w = tm.minWidth;
30168             }else{
30169                 w = aw;
30170             }
30171         }
30172         //tipBody.setWidth(w);
30173         el.setWidth(parseInt(w, 10) + p);
30174         if(ce.autoHide === false){
30175             close.setDisplayed(true);
30176             if(dd){
30177                 dd.unlock();
30178             }
30179         }else{
30180             close.setDisplayed(false);
30181             if(dd){
30182                 dd.lock();
30183             }
30184         }
30185         if(xy){
30186             el.avoidY = xy[1]-18;
30187             el.setXY(xy);
30188         }
30189         if(tm.animate){
30190             el.setOpacity(.1);
30191             el.setStyle("visibility", "visible");
30192             el.fadeIn({callback: afterShow});
30193         }else{
30194             afterShow();
30195         }
30196     };
30197     
30198     var afterShow = function(){
30199         if(ce){
30200             el.show();
30201             esc.enable();
30202             if(tm.autoDismiss && ce.autoHide !== false){
30203                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30204             }
30205         }
30206     };
30207     
30208     var hide = function(noanim){
30209         clearTimeout(dismissProc);
30210         clearTimeout(hideProc);
30211         ce = null;
30212         if(el.isVisible()){
30213             esc.disable();
30214             if(noanim !== true && tm.animate){
30215                 el.fadeOut({callback: afterHide});
30216             }else{
30217                 afterHide();
30218             } 
30219         }
30220     };
30221     
30222     var afterHide = function(){
30223         el.hide();
30224         if(removeCls){
30225             el.removeClass(removeCls);
30226             removeCls = null;
30227         }
30228     };
30229     
30230     return {
30231         /**
30232         * @cfg {Number} minWidth
30233         * The minimum width of the quick tip (defaults to 40)
30234         */
30235        minWidth : 40,
30236         /**
30237         * @cfg {Number} maxWidth
30238         * The maximum width of the quick tip (defaults to 300)
30239         */
30240        maxWidth : 300,
30241         /**
30242         * @cfg {Boolean} interceptTitles
30243         * True to automatically use the element's DOM title value if available (defaults to false)
30244         */
30245        interceptTitles : false,
30246         /**
30247         * @cfg {Boolean} trackMouse
30248         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30249         */
30250        trackMouse : false,
30251         /**
30252         * @cfg {Boolean} hideOnClick
30253         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30254         */
30255        hideOnClick : true,
30256         /**
30257         * @cfg {Number} showDelay
30258         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30259         */
30260        showDelay : 500,
30261         /**
30262         * @cfg {Number} hideDelay
30263         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30264         */
30265        hideDelay : 200,
30266         /**
30267         * @cfg {Boolean} autoHide
30268         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30269         * Used in conjunction with hideDelay.
30270         */
30271        autoHide : true,
30272         /**
30273         * @cfg {Boolean}
30274         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30275         * (defaults to true).  Used in conjunction with autoDismissDelay.
30276         */
30277        autoDismiss : true,
30278         /**
30279         * @cfg {Number}
30280         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30281         */
30282        autoDismissDelay : 5000,
30283        /**
30284         * @cfg {Boolean} animate
30285         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30286         */
30287        animate : false,
30288
30289        /**
30290         * @cfg {String} title
30291         * Title text to display (defaults to '').  This can be any valid HTML markup.
30292         */
30293         title: '',
30294        /**
30295         * @cfg {String} text
30296         * Body text to display (defaults to '').  This can be any valid HTML markup.
30297         */
30298         text : '',
30299        /**
30300         * @cfg {String} cls
30301         * A CSS class to apply to the base quick tip element (defaults to '').
30302         */
30303         cls : '',
30304        /**
30305         * @cfg {Number} width
30306         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30307         * minWidth or maxWidth.
30308         */
30309         width : null,
30310
30311     /**
30312      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30313      * or display QuickTips in a page.
30314      */
30315        init : function(){
30316           tm = Roo.QuickTips;
30317           cfg = tm.tagConfig;
30318           if(!inited){
30319               if(!Roo.isReady){ // allow calling of init() before onReady
30320                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30321                   return;
30322               }
30323               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30324               el.fxDefaults = {stopFx: true};
30325               // maximum custom styling
30326               //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>');
30327               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>');              
30328               tipTitle = el.child('h3');
30329               tipTitle.enableDisplayMode("block");
30330               tipBody = el.child('div.x-tip-bd');
30331               tipBodyText = el.child('div.x-tip-bd-inner');
30332               //bdLeft = el.child('div.x-tip-bd-left');
30333               //bdRight = el.child('div.x-tip-bd-right');
30334               close = el.child('div.x-tip-close');
30335               close.enableDisplayMode("block");
30336               close.on("click", hide);
30337               var d = Roo.get(document);
30338               d.on("mousedown", onDown);
30339               d.on("mouseover", onOver);
30340               d.on("mouseout", onOut);
30341               d.on("mousemove", onMove);
30342               esc = d.addKeyListener(27, hide);
30343               esc.disable();
30344               if(Roo.dd.DD){
30345                   dd = el.initDD("default", null, {
30346                       onDrag : function(){
30347                           el.sync();  
30348                       }
30349                   });
30350                   dd.setHandleElId(tipTitle.id);
30351                   dd.lock();
30352               }
30353               inited = true;
30354           }
30355           this.enable(); 
30356        },
30357
30358     /**
30359      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30360      * are supported:
30361      * <pre>
30362 Property    Type                   Description
30363 ----------  ---------------------  ------------------------------------------------------------------------
30364 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30365      * </ul>
30366      * @param {Object} config The config object
30367      */
30368        register : function(config){
30369            var cs = config instanceof Array ? config : arguments;
30370            for(var i = 0, len = cs.length; i < len; i++) {
30371                var c = cs[i];
30372                var target = c.target;
30373                if(target){
30374                    if(target instanceof Array){
30375                        for(var j = 0, jlen = target.length; j < jlen; j++){
30376                            tagEls[target[j]] = c;
30377                        }
30378                    }else{
30379                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30380                    }
30381                }
30382            }
30383        },
30384
30385     /**
30386      * Removes this quick tip from its element and destroys it.
30387      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30388      */
30389        unregister : function(el){
30390            delete tagEls[Roo.id(el)];
30391        },
30392
30393     /**
30394      * Enable this quick tip.
30395      */
30396        enable : function(){
30397            if(inited && disabled){
30398                locks.pop();
30399                if(locks.length < 1){
30400                    disabled = false;
30401                }
30402            }
30403        },
30404
30405     /**
30406      * Disable this quick tip.
30407      */
30408        disable : function(){
30409           disabled = true;
30410           clearTimeout(showProc);
30411           clearTimeout(hideProc);
30412           clearTimeout(dismissProc);
30413           if(ce){
30414               hide(true);
30415           }
30416           locks.push(1);
30417        },
30418
30419     /**
30420      * Returns true if the quick tip is enabled, else false.
30421      */
30422        isEnabled : function(){
30423             return !disabled;
30424        },
30425
30426         // private
30427        tagConfig : {
30428            namespace : "ext",
30429            attribute : "qtip",
30430            width : "width",
30431            target : "target",
30432            title : "qtitle",
30433            hide : "hide",
30434            cls : "qclass"
30435        }
30436    };
30437 }();
30438
30439 // backwards compat
30440 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30441  * Based on:
30442  * Ext JS Library 1.1.1
30443  * Copyright(c) 2006-2007, Ext JS, LLC.
30444  *
30445  * Originally Released Under LGPL - original licence link has changed is not relivant.
30446  *
30447  * Fork - LGPL
30448  * <script type="text/javascript">
30449  */
30450  
30451
30452 /**
30453  * @class Roo.tree.TreePanel
30454  * @extends Roo.data.Tree
30455
30456  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30457  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30458  * @cfg {Boolean} enableDD true to enable drag and drop
30459  * @cfg {Boolean} enableDrag true to enable just drag
30460  * @cfg {Boolean} enableDrop true to enable just drop
30461  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30462  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30463  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30464  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30465  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30466  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30467  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30468  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30469  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30470  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30471  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30472  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30473  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30474  * @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>
30475  * @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>
30476  * 
30477  * @constructor
30478  * @param {String/HTMLElement/Element} el The container element
30479  * @param {Object} config
30480  */
30481 Roo.tree.TreePanel = function(el, config){
30482     var root = false;
30483     var loader = false;
30484     if (config.root) {
30485         root = config.root;
30486         delete config.root;
30487     }
30488     if (config.loader) {
30489         loader = config.loader;
30490         delete config.loader;
30491     }
30492     
30493     Roo.apply(this, config);
30494     Roo.tree.TreePanel.superclass.constructor.call(this);
30495     this.el = Roo.get(el);
30496     this.el.addClass('x-tree');
30497     //console.log(root);
30498     if (root) {
30499         this.setRootNode( Roo.factory(root, Roo.tree));
30500     }
30501     if (loader) {
30502         this.loader = Roo.factory(loader, Roo.tree);
30503     }
30504    /**
30505     * Read-only. The id of the container element becomes this TreePanel's id.
30506     */
30507    this.id = this.el.id;
30508    this.addEvents({
30509         /**
30510         * @event beforeload
30511         * Fires before a node is loaded, return false to cancel
30512         * @param {Node} node The node being loaded
30513         */
30514         "beforeload" : true,
30515         /**
30516         * @event load
30517         * Fires when a node is loaded
30518         * @param {Node} node The node that was loaded
30519         */
30520         "load" : true,
30521         /**
30522         * @event textchange
30523         * Fires when the text for a node is changed
30524         * @param {Node} node The node
30525         * @param {String} text The new text
30526         * @param {String} oldText The old text
30527         */
30528         "textchange" : true,
30529         /**
30530         * @event beforeexpand
30531         * Fires before a node is expanded, return false to cancel.
30532         * @param {Node} node The node
30533         * @param {Boolean} deep
30534         * @param {Boolean} anim
30535         */
30536         "beforeexpand" : true,
30537         /**
30538         * @event beforecollapse
30539         * Fires before a node is collapsed, return false to cancel.
30540         * @param {Node} node The node
30541         * @param {Boolean} deep
30542         * @param {Boolean} anim
30543         */
30544         "beforecollapse" : true,
30545         /**
30546         * @event expand
30547         * Fires when a node is expanded
30548         * @param {Node} node The node
30549         */
30550         "expand" : true,
30551         /**
30552         * @event disabledchange
30553         * Fires when the disabled status of a node changes
30554         * @param {Node} node The node
30555         * @param {Boolean} disabled
30556         */
30557         "disabledchange" : true,
30558         /**
30559         * @event collapse
30560         * Fires when a node is collapsed
30561         * @param {Node} node The node
30562         */
30563         "collapse" : true,
30564         /**
30565         * @event beforeclick
30566         * Fires before click processing on a node. Return false to cancel the default action.
30567         * @param {Node} node The node
30568         * @param {Roo.EventObject} e The event object
30569         */
30570         "beforeclick":true,
30571         /**
30572         * @event checkchange
30573         * Fires when a node with a checkbox's checked property changes
30574         * @param {Node} this This node
30575         * @param {Boolean} checked
30576         */
30577         "checkchange":true,
30578         /**
30579         * @event click
30580         * Fires when a node is clicked
30581         * @param {Node} node The node
30582         * @param {Roo.EventObject} e The event object
30583         */
30584         "click":true,
30585         /**
30586         * @event dblclick
30587         * Fires when a node is double clicked
30588         * @param {Node} node The node
30589         * @param {Roo.EventObject} e The event object
30590         */
30591         "dblclick":true,
30592         /**
30593         * @event contextmenu
30594         * Fires when a node is right clicked
30595         * @param {Node} node The node
30596         * @param {Roo.EventObject} e The event object
30597         */
30598         "contextmenu":true,
30599         /**
30600         * @event beforechildrenrendered
30601         * Fires right before the child nodes for a node are rendered
30602         * @param {Node} node The node
30603         */
30604         "beforechildrenrendered":true,
30605        /**
30606              * @event startdrag
30607              * Fires when a node starts being dragged
30608              * @param {Roo.tree.TreePanel} this
30609              * @param {Roo.tree.TreeNode} node
30610              * @param {event} e The raw browser event
30611              */ 
30612             "startdrag" : true,
30613             /**
30614              * @event enddrag
30615              * Fires when a drag operation is complete
30616              * @param {Roo.tree.TreePanel} this
30617              * @param {Roo.tree.TreeNode} node
30618              * @param {event} e The raw browser event
30619              */
30620             "enddrag" : true,
30621             /**
30622              * @event dragdrop
30623              * Fires when a dragged node is dropped on a valid DD target
30624              * @param {Roo.tree.TreePanel} this
30625              * @param {Roo.tree.TreeNode} node
30626              * @param {DD} dd The dd it was dropped on
30627              * @param {event} e The raw browser event
30628              */
30629             "dragdrop" : true,
30630             /**
30631              * @event beforenodedrop
30632              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30633              * passed to handlers has the following properties:<br />
30634              * <ul style="padding:5px;padding-left:16px;">
30635              * <li>tree - The TreePanel</li>
30636              * <li>target - The node being targeted for the drop</li>
30637              * <li>data - The drag data from the drag source</li>
30638              * <li>point - The point of the drop - append, above or below</li>
30639              * <li>source - The drag source</li>
30640              * <li>rawEvent - Raw mouse event</li>
30641              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30642              * to be inserted by setting them on this object.</li>
30643              * <li>cancel - Set this to true to cancel the drop.</li>
30644              * </ul>
30645              * @param {Object} dropEvent
30646              */
30647             "beforenodedrop" : true,
30648             /**
30649              * @event nodedrop
30650              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30651              * passed to handlers has the following properties:<br />
30652              * <ul style="padding:5px;padding-left:16px;">
30653              * <li>tree - The TreePanel</li>
30654              * <li>target - The node being targeted for the drop</li>
30655              * <li>data - The drag data from the drag source</li>
30656              * <li>point - The point of the drop - append, above or below</li>
30657              * <li>source - The drag source</li>
30658              * <li>rawEvent - Raw mouse event</li>
30659              * <li>dropNode - Dropped node(s).</li>
30660              * </ul>
30661              * @param {Object} dropEvent
30662              */
30663             "nodedrop" : true,
30664              /**
30665              * @event nodedragover
30666              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30667              * passed to handlers has the following properties:<br />
30668              * <ul style="padding:5px;padding-left:16px;">
30669              * <li>tree - The TreePanel</li>
30670              * <li>target - The node being targeted for the drop</li>
30671              * <li>data - The drag data from the drag source</li>
30672              * <li>point - The point of the drop - append, above or below</li>
30673              * <li>source - The drag source</li>
30674              * <li>rawEvent - Raw mouse event</li>
30675              * <li>dropNode - Drop node(s) provided by the source.</li>
30676              * <li>cancel - Set this to true to signal drop not allowed.</li>
30677              * </ul>
30678              * @param {Object} dragOverEvent
30679              */
30680             "nodedragover" : true
30681         
30682    });
30683    if(this.singleExpand){
30684        this.on("beforeexpand", this.restrictExpand, this);
30685    }
30686 };
30687 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30688     rootVisible : true,
30689     animate: Roo.enableFx,
30690     lines : true,
30691     enableDD : false,
30692     hlDrop : Roo.enableFx,
30693   
30694     renderer: false,
30695     
30696     rendererTip: false,
30697     // private
30698     restrictExpand : function(node){
30699         var p = node.parentNode;
30700         if(p){
30701             if(p.expandedChild && p.expandedChild.parentNode == p){
30702                 p.expandedChild.collapse();
30703             }
30704             p.expandedChild = node;
30705         }
30706     },
30707
30708     // private override
30709     setRootNode : function(node){
30710         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30711         if(!this.rootVisible){
30712             node.ui = new Roo.tree.RootTreeNodeUI(node);
30713         }
30714         return node;
30715     },
30716
30717     /**
30718      * Returns the container element for this TreePanel
30719      */
30720     getEl : function(){
30721         return this.el;
30722     },
30723
30724     /**
30725      * Returns the default TreeLoader for this TreePanel
30726      */
30727     getLoader : function(){
30728         return this.loader;
30729     },
30730
30731     /**
30732      * Expand all nodes
30733      */
30734     expandAll : function(){
30735         this.root.expand(true);
30736     },
30737
30738     /**
30739      * Collapse all nodes
30740      */
30741     collapseAll : function(){
30742         this.root.collapse(true);
30743     },
30744
30745     /**
30746      * Returns the selection model used by this TreePanel
30747      */
30748     getSelectionModel : function(){
30749         if(!this.selModel){
30750             this.selModel = new Roo.tree.DefaultSelectionModel();
30751         }
30752         return this.selModel;
30753     },
30754
30755     /**
30756      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30757      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30758      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30759      * @return {Array}
30760      */
30761     getChecked : function(a, startNode){
30762         startNode = startNode || this.root;
30763         var r = [];
30764         var f = function(){
30765             if(this.attributes.checked){
30766                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30767             }
30768         }
30769         startNode.cascade(f);
30770         return r;
30771     },
30772
30773     /**
30774      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30775      * @param {String} path
30776      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30777      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30778      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30779      */
30780     expandPath : function(path, attr, callback){
30781         attr = attr || "id";
30782         var keys = path.split(this.pathSeparator);
30783         var curNode = this.root;
30784         if(curNode.attributes[attr] != keys[1]){ // invalid root
30785             if(callback){
30786                 callback(false, null);
30787             }
30788             return;
30789         }
30790         var index = 1;
30791         var f = function(){
30792             if(++index == keys.length){
30793                 if(callback){
30794                     callback(true, curNode);
30795                 }
30796                 return;
30797             }
30798             var c = curNode.findChild(attr, keys[index]);
30799             if(!c){
30800                 if(callback){
30801                     callback(false, curNode);
30802                 }
30803                 return;
30804             }
30805             curNode = c;
30806             c.expand(false, false, f);
30807         };
30808         curNode.expand(false, false, f);
30809     },
30810
30811     /**
30812      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30813      * @param {String} path
30814      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30815      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30816      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30817      */
30818     selectPath : function(path, attr, callback){
30819         attr = attr || "id";
30820         var keys = path.split(this.pathSeparator);
30821         var v = keys.pop();
30822         if(keys.length > 0){
30823             var f = function(success, node){
30824                 if(success && node){
30825                     var n = node.findChild(attr, v);
30826                     if(n){
30827                         n.select();
30828                         if(callback){
30829                             callback(true, n);
30830                         }
30831                     }else if(callback){
30832                         callback(false, n);
30833                     }
30834                 }else{
30835                     if(callback){
30836                         callback(false, n);
30837                     }
30838                 }
30839             };
30840             this.expandPath(keys.join(this.pathSeparator), attr, f);
30841         }else{
30842             this.root.select();
30843             if(callback){
30844                 callback(true, this.root);
30845             }
30846         }
30847     },
30848
30849     getTreeEl : function(){
30850         return this.el;
30851     },
30852
30853     /**
30854      * Trigger rendering of this TreePanel
30855      */
30856     render : function(){
30857         if (this.innerCt) {
30858             return this; // stop it rendering more than once!!
30859         }
30860         
30861         this.innerCt = this.el.createChild({tag:"ul",
30862                cls:"x-tree-root-ct " +
30863                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30864
30865         if(this.containerScroll){
30866             Roo.dd.ScrollManager.register(this.el);
30867         }
30868         if((this.enableDD || this.enableDrop) && !this.dropZone){
30869            /**
30870             * The dropZone used by this tree if drop is enabled
30871             * @type Roo.tree.TreeDropZone
30872             */
30873              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30874                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30875            });
30876         }
30877         if((this.enableDD || this.enableDrag) && !this.dragZone){
30878            /**
30879             * The dragZone used by this tree if drag is enabled
30880             * @type Roo.tree.TreeDragZone
30881             */
30882             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30883                ddGroup: this.ddGroup || "TreeDD",
30884                scroll: this.ddScroll
30885            });
30886         }
30887         this.getSelectionModel().init(this);
30888         if (!this.root) {
30889             console.log("ROOT not set in tree");
30890             return;
30891         }
30892         this.root.render();
30893         if(!this.rootVisible){
30894             this.root.renderChildren();
30895         }
30896         return this;
30897     }
30898 });/*
30899  * Based on:
30900  * Ext JS Library 1.1.1
30901  * Copyright(c) 2006-2007, Ext JS, LLC.
30902  *
30903  * Originally Released Under LGPL - original licence link has changed is not relivant.
30904  *
30905  * Fork - LGPL
30906  * <script type="text/javascript">
30907  */
30908  
30909
30910 /**
30911  * @class Roo.tree.DefaultSelectionModel
30912  * @extends Roo.util.Observable
30913  * The default single selection for a TreePanel.
30914  */
30915 Roo.tree.DefaultSelectionModel = function(){
30916    this.selNode = null;
30917    
30918    this.addEvents({
30919        /**
30920         * @event selectionchange
30921         * Fires when the selected node changes
30922         * @param {DefaultSelectionModel} this
30923         * @param {TreeNode} node the new selection
30924         */
30925        "selectionchange" : true,
30926
30927        /**
30928         * @event beforeselect
30929         * Fires before the selected node changes, return false to cancel the change
30930         * @param {DefaultSelectionModel} this
30931         * @param {TreeNode} node the new selection
30932         * @param {TreeNode} node the old selection
30933         */
30934        "beforeselect" : true
30935    });
30936 };
30937
30938 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30939     init : function(tree){
30940         this.tree = tree;
30941         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30942         tree.on("click", this.onNodeClick, this);
30943     },
30944     
30945     onNodeClick : function(node, e){
30946         if (e.ctrlKey && this.selNode == node)  {
30947             this.unselect(node);
30948             return;
30949         }
30950         this.select(node);
30951     },
30952     
30953     /**
30954      * Select a node.
30955      * @param {TreeNode} node The node to select
30956      * @return {TreeNode} The selected node
30957      */
30958     select : function(node){
30959         var last = this.selNode;
30960         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30961             if(last){
30962                 last.ui.onSelectedChange(false);
30963             }
30964             this.selNode = node;
30965             node.ui.onSelectedChange(true);
30966             this.fireEvent("selectionchange", this, node, last);
30967         }
30968         return node;
30969     },
30970     
30971     /**
30972      * Deselect a node.
30973      * @param {TreeNode} node The node to unselect
30974      */
30975     unselect : function(node){
30976         if(this.selNode == node){
30977             this.clearSelections();
30978         }    
30979     },
30980     
30981     /**
30982      * Clear all selections
30983      */
30984     clearSelections : function(){
30985         var n = this.selNode;
30986         if(n){
30987             n.ui.onSelectedChange(false);
30988             this.selNode = null;
30989             this.fireEvent("selectionchange", this, null);
30990         }
30991         return n;
30992     },
30993     
30994     /**
30995      * Get the selected node
30996      * @return {TreeNode} The selected node
30997      */
30998     getSelectedNode : function(){
30999         return this.selNode;    
31000     },
31001     
31002     /**
31003      * Returns true if the node is selected
31004      * @param {TreeNode} node The node to check
31005      * @return {Boolean}
31006      */
31007     isSelected : function(node){
31008         return this.selNode == node;  
31009     },
31010
31011     /**
31012      * Selects the node above the selected node in the tree, intelligently walking the nodes
31013      * @return TreeNode The new selection
31014      */
31015     selectPrevious : function(){
31016         var s = this.selNode || this.lastSelNode;
31017         if(!s){
31018             return null;
31019         }
31020         var ps = s.previousSibling;
31021         if(ps){
31022             if(!ps.isExpanded() || ps.childNodes.length < 1){
31023                 return this.select(ps);
31024             } else{
31025                 var lc = ps.lastChild;
31026                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31027                     lc = lc.lastChild;
31028                 }
31029                 return this.select(lc);
31030             }
31031         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31032             return this.select(s.parentNode);
31033         }
31034         return null;
31035     },
31036
31037     /**
31038      * Selects the node above the selected node in the tree, intelligently walking the nodes
31039      * @return TreeNode The new selection
31040      */
31041     selectNext : function(){
31042         var s = this.selNode || this.lastSelNode;
31043         if(!s){
31044             return null;
31045         }
31046         if(s.firstChild && s.isExpanded()){
31047              return this.select(s.firstChild);
31048          }else if(s.nextSibling){
31049              return this.select(s.nextSibling);
31050          }else if(s.parentNode){
31051             var newS = null;
31052             s.parentNode.bubble(function(){
31053                 if(this.nextSibling){
31054                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31055                     return false;
31056                 }
31057             });
31058             return newS;
31059          }
31060         return null;
31061     },
31062
31063     onKeyDown : function(e){
31064         var s = this.selNode || this.lastSelNode;
31065         // undesirable, but required
31066         var sm = this;
31067         if(!s){
31068             return;
31069         }
31070         var k = e.getKey();
31071         switch(k){
31072              case e.DOWN:
31073                  e.stopEvent();
31074                  this.selectNext();
31075              break;
31076              case e.UP:
31077                  e.stopEvent();
31078                  this.selectPrevious();
31079              break;
31080              case e.RIGHT:
31081                  e.preventDefault();
31082                  if(s.hasChildNodes()){
31083                      if(!s.isExpanded()){
31084                          s.expand();
31085                      }else if(s.firstChild){
31086                          this.select(s.firstChild, e);
31087                      }
31088                  }
31089              break;
31090              case e.LEFT:
31091                  e.preventDefault();
31092                  if(s.hasChildNodes() && s.isExpanded()){
31093                      s.collapse();
31094                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31095                      this.select(s.parentNode, e);
31096                  }
31097              break;
31098         };
31099     }
31100 });
31101
31102 /**
31103  * @class Roo.tree.MultiSelectionModel
31104  * @extends Roo.util.Observable
31105  * Multi selection for a TreePanel.
31106  */
31107 Roo.tree.MultiSelectionModel = function(){
31108    this.selNodes = [];
31109    this.selMap = {};
31110    this.addEvents({
31111        /**
31112         * @event selectionchange
31113         * Fires when the selected nodes change
31114         * @param {MultiSelectionModel} this
31115         * @param {Array} nodes Array of the selected nodes
31116         */
31117        "selectionchange" : true
31118    });
31119 };
31120
31121 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31122     init : function(tree){
31123         this.tree = tree;
31124         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31125         tree.on("click", this.onNodeClick, this);
31126     },
31127     
31128     onNodeClick : function(node, e){
31129         this.select(node, e, e.ctrlKey);
31130     },
31131     
31132     /**
31133      * Select a node.
31134      * @param {TreeNode} node The node to select
31135      * @param {EventObject} e (optional) An event associated with the selection
31136      * @param {Boolean} keepExisting True to retain existing selections
31137      * @return {TreeNode} The selected node
31138      */
31139     select : function(node, e, keepExisting){
31140         if(keepExisting !== true){
31141             this.clearSelections(true);
31142         }
31143         if(this.isSelected(node)){
31144             this.lastSelNode = node;
31145             return node;
31146         }
31147         this.selNodes.push(node);
31148         this.selMap[node.id] = node;
31149         this.lastSelNode = node;
31150         node.ui.onSelectedChange(true);
31151         this.fireEvent("selectionchange", this, this.selNodes);
31152         return node;
31153     },
31154     
31155     /**
31156      * Deselect a node.
31157      * @param {TreeNode} node The node to unselect
31158      */
31159     unselect : function(node){
31160         if(this.selMap[node.id]){
31161             node.ui.onSelectedChange(false);
31162             var sn = this.selNodes;
31163             var index = -1;
31164             if(sn.indexOf){
31165                 index = sn.indexOf(node);
31166             }else{
31167                 for(var i = 0, len = sn.length; i < len; i++){
31168                     if(sn[i] == node){
31169                         index = i;
31170                         break;
31171                     }
31172                 }
31173             }
31174             if(index != -1){
31175                 this.selNodes.splice(index, 1);
31176             }
31177             delete this.selMap[node.id];
31178             this.fireEvent("selectionchange", this, this.selNodes);
31179         }
31180     },
31181     
31182     /**
31183      * Clear all selections
31184      */
31185     clearSelections : function(suppressEvent){
31186         var sn = this.selNodes;
31187         if(sn.length > 0){
31188             for(var i = 0, len = sn.length; i < len; i++){
31189                 sn[i].ui.onSelectedChange(false);
31190             }
31191             this.selNodes = [];
31192             this.selMap = {};
31193             if(suppressEvent !== true){
31194                 this.fireEvent("selectionchange", this, this.selNodes);
31195             }
31196         }
31197     },
31198     
31199     /**
31200      * Returns true if the node is selected
31201      * @param {TreeNode} node The node to check
31202      * @return {Boolean}
31203      */
31204     isSelected : function(node){
31205         return this.selMap[node.id] ? true : false;  
31206     },
31207     
31208     /**
31209      * Returns an array of the selected nodes
31210      * @return {Array}
31211      */
31212     getSelectedNodes : function(){
31213         return this.selNodes;    
31214     },
31215
31216     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31217
31218     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31219
31220     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31221 });/*
31222  * Based on:
31223  * Ext JS Library 1.1.1
31224  * Copyright(c) 2006-2007, Ext JS, LLC.
31225  *
31226  * Originally Released Under LGPL - original licence link has changed is not relivant.
31227  *
31228  * Fork - LGPL
31229  * <script type="text/javascript">
31230  */
31231  
31232 /**
31233  * @class Roo.tree.TreeNode
31234  * @extends Roo.data.Node
31235  * @cfg {String} text The text for this node
31236  * @cfg {Boolean} expanded true to start the node expanded
31237  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31238  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31239  * @cfg {Boolean} disabled true to start the node disabled
31240  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31241  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31242  * @cfg {String} cls A css class to be added to the node
31243  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31244  * @cfg {String} href URL of the link used for the node (defaults to #)
31245  * @cfg {String} hrefTarget target frame for the link
31246  * @cfg {String} qtip An Ext QuickTip for the node
31247  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31248  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31249  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31250  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31251  * (defaults to undefined with no checkbox rendered)
31252  * @constructor
31253  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31254  */
31255 Roo.tree.TreeNode = function(attributes){
31256     attributes = attributes || {};
31257     if(typeof attributes == "string"){
31258         attributes = {text: attributes};
31259     }
31260     this.childrenRendered = false;
31261     this.rendered = false;
31262     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31263     this.expanded = attributes.expanded === true;
31264     this.isTarget = attributes.isTarget !== false;
31265     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31266     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31267
31268     /**
31269      * Read-only. The text for this node. To change it use setText().
31270      * @type String
31271      */
31272     this.text = attributes.text;
31273     /**
31274      * True if this node is disabled.
31275      * @type Boolean
31276      */
31277     this.disabled = attributes.disabled === true;
31278
31279     this.addEvents({
31280         /**
31281         * @event textchange
31282         * Fires when the text for this node is changed
31283         * @param {Node} this This node
31284         * @param {String} text The new text
31285         * @param {String} oldText The old text
31286         */
31287         "textchange" : true,
31288         /**
31289         * @event beforeexpand
31290         * Fires before this node is expanded, return false to cancel.
31291         * @param {Node} this This node
31292         * @param {Boolean} deep
31293         * @param {Boolean} anim
31294         */
31295         "beforeexpand" : true,
31296         /**
31297         * @event beforecollapse
31298         * Fires before this node is collapsed, return false to cancel.
31299         * @param {Node} this This node
31300         * @param {Boolean} deep
31301         * @param {Boolean} anim
31302         */
31303         "beforecollapse" : true,
31304         /**
31305         * @event expand
31306         * Fires when this node is expanded
31307         * @param {Node} this This node
31308         */
31309         "expand" : true,
31310         /**
31311         * @event disabledchange
31312         * Fires when the disabled status of this node changes
31313         * @param {Node} this This node
31314         * @param {Boolean} disabled
31315         */
31316         "disabledchange" : true,
31317         /**
31318         * @event collapse
31319         * Fires when this node is collapsed
31320         * @param {Node} this This node
31321         */
31322         "collapse" : true,
31323         /**
31324         * @event beforeclick
31325         * Fires before click processing. Return false to cancel the default action.
31326         * @param {Node} this This node
31327         * @param {Roo.EventObject} e The event object
31328         */
31329         "beforeclick":true,
31330         /**
31331         * @event checkchange
31332         * Fires when a node with a checkbox's checked property changes
31333         * @param {Node} this This node
31334         * @param {Boolean} checked
31335         */
31336         "checkchange":true,
31337         /**
31338         * @event click
31339         * Fires when this node is clicked
31340         * @param {Node} this This node
31341         * @param {Roo.EventObject} e The event object
31342         */
31343         "click":true,
31344         /**
31345         * @event dblclick
31346         * Fires when this node is double clicked
31347         * @param {Node} this This node
31348         * @param {Roo.EventObject} e The event object
31349         */
31350         "dblclick":true,
31351         /**
31352         * @event contextmenu
31353         * Fires when this node is right clicked
31354         * @param {Node} this This node
31355         * @param {Roo.EventObject} e The event object
31356         */
31357         "contextmenu":true,
31358         /**
31359         * @event beforechildrenrendered
31360         * Fires right before the child nodes for this node are rendered
31361         * @param {Node} this This node
31362         */
31363         "beforechildrenrendered":true
31364     });
31365
31366     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31367
31368     /**
31369      * Read-only. The UI for this node
31370      * @type TreeNodeUI
31371      */
31372     this.ui = new uiClass(this);
31373 };
31374 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31375     preventHScroll: true,
31376     /**
31377      * Returns true if this node is expanded
31378      * @return {Boolean}
31379      */
31380     isExpanded : function(){
31381         return this.expanded;
31382     },
31383
31384     /**
31385      * Returns the UI object for this node
31386      * @return {TreeNodeUI}
31387      */
31388     getUI : function(){
31389         return this.ui;
31390     },
31391
31392     // private override
31393     setFirstChild : function(node){
31394         var of = this.firstChild;
31395         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31396         if(this.childrenRendered && of && node != of){
31397             of.renderIndent(true, true);
31398         }
31399         if(this.rendered){
31400             this.renderIndent(true, true);
31401         }
31402     },
31403
31404     // private override
31405     setLastChild : function(node){
31406         var ol = this.lastChild;
31407         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31408         if(this.childrenRendered && ol && node != ol){
31409             ol.renderIndent(true, true);
31410         }
31411         if(this.rendered){
31412             this.renderIndent(true, true);
31413         }
31414     },
31415
31416     // these methods are overridden to provide lazy rendering support
31417     // private override
31418     appendChild : function(){
31419         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31420         if(node && this.childrenRendered){
31421             node.render();
31422         }
31423         this.ui.updateExpandIcon();
31424         return node;
31425     },
31426
31427     // private override
31428     removeChild : function(node){
31429         this.ownerTree.getSelectionModel().unselect(node);
31430         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31431         // if it's been rendered remove dom node
31432         if(this.childrenRendered){
31433             node.ui.remove();
31434         }
31435         if(this.childNodes.length < 1){
31436             this.collapse(false, false);
31437         }else{
31438             this.ui.updateExpandIcon();
31439         }
31440         if(!this.firstChild) {
31441             this.childrenRendered = false;
31442         }
31443         return node;
31444     },
31445
31446     // private override
31447     insertBefore : function(node, refNode){
31448         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31449         if(newNode && refNode && this.childrenRendered){
31450             node.render();
31451         }
31452         this.ui.updateExpandIcon();
31453         return newNode;
31454     },
31455
31456     /**
31457      * Sets the text for this node
31458      * @param {String} text
31459      */
31460     setText : function(text){
31461         var oldText = this.text;
31462         this.text = text;
31463         this.attributes.text = text;
31464         if(this.rendered){ // event without subscribing
31465             this.ui.onTextChange(this, text, oldText);
31466         }
31467         this.fireEvent("textchange", this, text, oldText);
31468     },
31469
31470     /**
31471      * Triggers selection of this node
31472      */
31473     select : function(){
31474         this.getOwnerTree().getSelectionModel().select(this);
31475     },
31476
31477     /**
31478      * Triggers deselection of this node
31479      */
31480     unselect : function(){
31481         this.getOwnerTree().getSelectionModel().unselect(this);
31482     },
31483
31484     /**
31485      * Returns true if this node is selected
31486      * @return {Boolean}
31487      */
31488     isSelected : function(){
31489         return this.getOwnerTree().getSelectionModel().isSelected(this);
31490     },
31491
31492     /**
31493      * Expand this node.
31494      * @param {Boolean} deep (optional) True to expand all children as well
31495      * @param {Boolean} anim (optional) false to cancel the default animation
31496      * @param {Function} callback (optional) A callback to be called when
31497      * expanding this node completes (does not wait for deep expand to complete).
31498      * Called with 1 parameter, this node.
31499      */
31500     expand : function(deep, anim, callback){
31501         if(!this.expanded){
31502             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31503                 return;
31504             }
31505             if(!this.childrenRendered){
31506                 this.renderChildren();
31507             }
31508             this.expanded = true;
31509             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31510                 this.ui.animExpand(function(){
31511                     this.fireEvent("expand", this);
31512                     if(typeof callback == "function"){
31513                         callback(this);
31514                     }
31515                     if(deep === true){
31516                         this.expandChildNodes(true);
31517                     }
31518                 }.createDelegate(this));
31519                 return;
31520             }else{
31521                 this.ui.expand();
31522                 this.fireEvent("expand", this);
31523                 if(typeof callback == "function"){
31524                     callback(this);
31525                 }
31526             }
31527         }else{
31528            if(typeof callback == "function"){
31529                callback(this);
31530            }
31531         }
31532         if(deep === true){
31533             this.expandChildNodes(true);
31534         }
31535     },
31536
31537     isHiddenRoot : function(){
31538         return this.isRoot && !this.getOwnerTree().rootVisible;
31539     },
31540
31541     /**
31542      * Collapse this node.
31543      * @param {Boolean} deep (optional) True to collapse all children as well
31544      * @param {Boolean} anim (optional) false to cancel the default animation
31545      */
31546     collapse : function(deep, anim){
31547         if(this.expanded && !this.isHiddenRoot()){
31548             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31549                 return;
31550             }
31551             this.expanded = false;
31552             if((this.getOwnerTree().animate && anim !== false) || anim){
31553                 this.ui.animCollapse(function(){
31554                     this.fireEvent("collapse", this);
31555                     if(deep === true){
31556                         this.collapseChildNodes(true);
31557                     }
31558                 }.createDelegate(this));
31559                 return;
31560             }else{
31561                 this.ui.collapse();
31562                 this.fireEvent("collapse", this);
31563             }
31564         }
31565         if(deep === true){
31566             var cs = this.childNodes;
31567             for(var i = 0, len = cs.length; i < len; i++) {
31568                 cs[i].collapse(true, false);
31569             }
31570         }
31571     },
31572
31573     // private
31574     delayedExpand : function(delay){
31575         if(!this.expandProcId){
31576             this.expandProcId = this.expand.defer(delay, this);
31577         }
31578     },
31579
31580     // private
31581     cancelExpand : function(){
31582         if(this.expandProcId){
31583             clearTimeout(this.expandProcId);
31584         }
31585         this.expandProcId = false;
31586     },
31587
31588     /**
31589      * Toggles expanded/collapsed state of the node
31590      */
31591     toggle : function(){
31592         if(this.expanded){
31593             this.collapse();
31594         }else{
31595             this.expand();
31596         }
31597     },
31598
31599     /**
31600      * Ensures all parent nodes are expanded
31601      */
31602     ensureVisible : function(callback){
31603         var tree = this.getOwnerTree();
31604         tree.expandPath(this.parentNode.getPath(), false, function(){
31605             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31606             Roo.callback(callback);
31607         }.createDelegate(this));
31608     },
31609
31610     /**
31611      * Expand all child nodes
31612      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31613      */
31614     expandChildNodes : function(deep){
31615         var cs = this.childNodes;
31616         for(var i = 0, len = cs.length; i < len; i++) {
31617                 cs[i].expand(deep);
31618         }
31619     },
31620
31621     /**
31622      * Collapse all child nodes
31623      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31624      */
31625     collapseChildNodes : function(deep){
31626         var cs = this.childNodes;
31627         for(var i = 0, len = cs.length; i < len; i++) {
31628                 cs[i].collapse(deep);
31629         }
31630     },
31631
31632     /**
31633      * Disables this node
31634      */
31635     disable : function(){
31636         this.disabled = true;
31637         this.unselect();
31638         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31639             this.ui.onDisableChange(this, true);
31640         }
31641         this.fireEvent("disabledchange", this, true);
31642     },
31643
31644     /**
31645      * Enables this node
31646      */
31647     enable : function(){
31648         this.disabled = false;
31649         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31650             this.ui.onDisableChange(this, false);
31651         }
31652         this.fireEvent("disabledchange", this, false);
31653     },
31654
31655     // private
31656     renderChildren : function(suppressEvent){
31657         if(suppressEvent !== false){
31658             this.fireEvent("beforechildrenrendered", this);
31659         }
31660         var cs = this.childNodes;
31661         for(var i = 0, len = cs.length; i < len; i++){
31662             cs[i].render(true);
31663         }
31664         this.childrenRendered = true;
31665     },
31666
31667     // private
31668     sort : function(fn, scope){
31669         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31670         if(this.childrenRendered){
31671             var cs = this.childNodes;
31672             for(var i = 0, len = cs.length; i < len; i++){
31673                 cs[i].render(true);
31674             }
31675         }
31676     },
31677
31678     // private
31679     render : function(bulkRender){
31680         this.ui.render(bulkRender);
31681         if(!this.rendered){
31682             this.rendered = true;
31683             if(this.expanded){
31684                 this.expanded = false;
31685                 this.expand(false, false);
31686             }
31687         }
31688     },
31689
31690     // private
31691     renderIndent : function(deep, refresh){
31692         if(refresh){
31693             this.ui.childIndent = null;
31694         }
31695         this.ui.renderIndent();
31696         if(deep === true && this.childrenRendered){
31697             var cs = this.childNodes;
31698             for(var i = 0, len = cs.length; i < len; i++){
31699                 cs[i].renderIndent(true, refresh);
31700             }
31701         }
31702     }
31703 });/*
31704  * Based on:
31705  * Ext JS Library 1.1.1
31706  * Copyright(c) 2006-2007, Ext JS, LLC.
31707  *
31708  * Originally Released Under LGPL - original licence link has changed is not relivant.
31709  *
31710  * Fork - LGPL
31711  * <script type="text/javascript">
31712  */
31713  
31714 /**
31715  * @class Roo.tree.AsyncTreeNode
31716  * @extends Roo.tree.TreeNode
31717  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31718  * @constructor
31719  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31720  */
31721  Roo.tree.AsyncTreeNode = function(config){
31722     this.loaded = false;
31723     this.loading = false;
31724     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31725     /**
31726     * @event beforeload
31727     * Fires before this node is loaded, return false to cancel
31728     * @param {Node} this This node
31729     */
31730     this.addEvents({'beforeload':true, 'load': true});
31731     /**
31732     * @event load
31733     * Fires when this node is loaded
31734     * @param {Node} this This node
31735     */
31736     /**
31737      * The loader used by this node (defaults to using the tree's defined loader)
31738      * @type TreeLoader
31739      * @property loader
31740      */
31741 };
31742 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31743     expand : function(deep, anim, callback){
31744         if(this.loading){ // if an async load is already running, waiting til it's done
31745             var timer;
31746             var f = function(){
31747                 if(!this.loading){ // done loading
31748                     clearInterval(timer);
31749                     this.expand(deep, anim, callback);
31750                 }
31751             }.createDelegate(this);
31752             timer = setInterval(f, 200);
31753             return;
31754         }
31755         if(!this.loaded){
31756             if(this.fireEvent("beforeload", this) === false){
31757                 return;
31758             }
31759             this.loading = true;
31760             this.ui.beforeLoad(this);
31761             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31762             if(loader){
31763                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31764                 return;
31765             }
31766         }
31767         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31768     },
31769     
31770     /**
31771      * Returns true if this node is currently loading
31772      * @return {Boolean}
31773      */
31774     isLoading : function(){
31775         return this.loading;  
31776     },
31777     
31778     loadComplete : function(deep, anim, callback){
31779         this.loading = false;
31780         this.loaded = true;
31781         this.ui.afterLoad(this);
31782         this.fireEvent("load", this);
31783         this.expand(deep, anim, callback);
31784     },
31785     
31786     /**
31787      * Returns true if this node has been loaded
31788      * @return {Boolean}
31789      */
31790     isLoaded : function(){
31791         return this.loaded;
31792     },
31793     
31794     hasChildNodes : function(){
31795         if(!this.isLeaf() && !this.loaded){
31796             return true;
31797         }else{
31798             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31799         }
31800     },
31801
31802     /**
31803      * Trigger a reload for this node
31804      * @param {Function} callback
31805      */
31806     reload : function(callback){
31807         this.collapse(false, false);
31808         while(this.firstChild){
31809             this.removeChild(this.firstChild);
31810         }
31811         this.childrenRendered = false;
31812         this.loaded = false;
31813         if(this.isHiddenRoot()){
31814             this.expanded = false;
31815         }
31816         this.expand(false, false, callback);
31817     }
31818 });/*
31819  * Based on:
31820  * Ext JS Library 1.1.1
31821  * Copyright(c) 2006-2007, Ext JS, LLC.
31822  *
31823  * Originally Released Under LGPL - original licence link has changed is not relivant.
31824  *
31825  * Fork - LGPL
31826  * <script type="text/javascript">
31827  */
31828  
31829 /**
31830  * @class Roo.tree.TreeNodeUI
31831  * @constructor
31832  * @param {Object} node The node to render
31833  * The TreeNode UI implementation is separate from the
31834  * tree implementation. Unless you are customizing the tree UI,
31835  * you should never have to use this directly.
31836  */
31837 Roo.tree.TreeNodeUI = function(node){
31838     this.node = node;
31839     this.rendered = false;
31840     this.animating = false;
31841     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31842 };
31843
31844 Roo.tree.TreeNodeUI.prototype = {
31845     removeChild : function(node){
31846         if(this.rendered){
31847             this.ctNode.removeChild(node.ui.getEl());
31848         }
31849     },
31850
31851     beforeLoad : function(){
31852          this.addClass("x-tree-node-loading");
31853     },
31854
31855     afterLoad : function(){
31856          this.removeClass("x-tree-node-loading");
31857     },
31858
31859     onTextChange : function(node, text, oldText){
31860         if(this.rendered){
31861             this.textNode.innerHTML = text;
31862         }
31863     },
31864
31865     onDisableChange : function(node, state){
31866         this.disabled = state;
31867         if(state){
31868             this.addClass("x-tree-node-disabled");
31869         }else{
31870             this.removeClass("x-tree-node-disabled");
31871         }
31872     },
31873
31874     onSelectedChange : function(state){
31875         if(state){
31876             this.focus();
31877             this.addClass("x-tree-selected");
31878         }else{
31879             //this.blur();
31880             this.removeClass("x-tree-selected");
31881         }
31882     },
31883
31884     onMove : function(tree, node, oldParent, newParent, index, refNode){
31885         this.childIndent = null;
31886         if(this.rendered){
31887             var targetNode = newParent.ui.getContainer();
31888             if(!targetNode){//target not rendered
31889                 this.holder = document.createElement("div");
31890                 this.holder.appendChild(this.wrap);
31891                 return;
31892             }
31893             var insertBefore = refNode ? refNode.ui.getEl() : null;
31894             if(insertBefore){
31895                 targetNode.insertBefore(this.wrap, insertBefore);
31896             }else{
31897                 targetNode.appendChild(this.wrap);
31898             }
31899             this.node.renderIndent(true);
31900         }
31901     },
31902
31903     addClass : function(cls){
31904         if(this.elNode){
31905             Roo.fly(this.elNode).addClass(cls);
31906         }
31907     },
31908
31909     removeClass : function(cls){
31910         if(this.elNode){
31911             Roo.fly(this.elNode).removeClass(cls);
31912         }
31913     },
31914
31915     remove : function(){
31916         if(this.rendered){
31917             this.holder = document.createElement("div");
31918             this.holder.appendChild(this.wrap);
31919         }
31920     },
31921
31922     fireEvent : function(){
31923         return this.node.fireEvent.apply(this.node, arguments);
31924     },
31925
31926     initEvents : function(){
31927         this.node.on("move", this.onMove, this);
31928         var E = Roo.EventManager;
31929         var a = this.anchor;
31930
31931         var el = Roo.fly(a, '_treeui');
31932
31933         if(Roo.isOpera){ // opera render bug ignores the CSS
31934             el.setStyle("text-decoration", "none");
31935         }
31936
31937         el.on("click", this.onClick, this);
31938         el.on("dblclick", this.onDblClick, this);
31939
31940         if(this.checkbox){
31941             Roo.EventManager.on(this.checkbox,
31942                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31943         }
31944
31945         el.on("contextmenu", this.onContextMenu, this);
31946
31947         var icon = Roo.fly(this.iconNode);
31948         icon.on("click", this.onClick, this);
31949         icon.on("dblclick", this.onDblClick, this);
31950         icon.on("contextmenu", this.onContextMenu, this);
31951         E.on(this.ecNode, "click", this.ecClick, this, true);
31952
31953         if(this.node.disabled){
31954             this.addClass("x-tree-node-disabled");
31955         }
31956         if(this.node.hidden){
31957             this.addClass("x-tree-node-disabled");
31958         }
31959         var ot = this.node.getOwnerTree();
31960         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31961         if(dd && (!this.node.isRoot || ot.rootVisible)){
31962             Roo.dd.Registry.register(this.elNode, {
31963                 node: this.node,
31964                 handles: this.getDDHandles(),
31965                 isHandle: false
31966             });
31967         }
31968     },
31969
31970     getDDHandles : function(){
31971         return [this.iconNode, this.textNode];
31972     },
31973
31974     hide : function(){
31975         if(this.rendered){
31976             this.wrap.style.display = "none";
31977         }
31978     },
31979
31980     show : function(){
31981         if(this.rendered){
31982             this.wrap.style.display = "";
31983         }
31984     },
31985
31986     onContextMenu : function(e){
31987         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31988             e.preventDefault();
31989             this.focus();
31990             this.fireEvent("contextmenu", this.node, e);
31991         }
31992     },
31993
31994     onClick : function(e){
31995         if(this.dropping){
31996             e.stopEvent();
31997             return;
31998         }
31999         if(this.fireEvent("beforeclick", this.node, e) !== false){
32000             if(!this.disabled && this.node.attributes.href){
32001                 this.fireEvent("click", this.node, e);
32002                 return;
32003             }
32004             e.preventDefault();
32005             if(this.disabled){
32006                 return;
32007             }
32008
32009             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32010                 this.node.toggle();
32011             }
32012
32013             this.fireEvent("click", this.node, e);
32014         }else{
32015             e.stopEvent();
32016         }
32017     },
32018
32019     onDblClick : function(e){
32020         e.preventDefault();
32021         if(this.disabled){
32022             return;
32023         }
32024         if(this.checkbox){
32025             this.toggleCheck();
32026         }
32027         if(!this.animating && this.node.hasChildNodes()){
32028             this.node.toggle();
32029         }
32030         this.fireEvent("dblclick", this.node, e);
32031     },
32032
32033     onCheckChange : function(){
32034         var checked = this.checkbox.checked;
32035         this.node.attributes.checked = checked;
32036         this.fireEvent('checkchange', this.node, checked);
32037     },
32038
32039     ecClick : function(e){
32040         if(!this.animating && this.node.hasChildNodes()){
32041             this.node.toggle();
32042         }
32043     },
32044
32045     startDrop : function(){
32046         this.dropping = true;
32047     },
32048
32049     // delayed drop so the click event doesn't get fired on a drop
32050     endDrop : function(){
32051        setTimeout(function(){
32052            this.dropping = false;
32053        }.createDelegate(this), 50);
32054     },
32055
32056     expand : function(){
32057         this.updateExpandIcon();
32058         this.ctNode.style.display = "";
32059     },
32060
32061     focus : function(){
32062         if(!this.node.preventHScroll){
32063             try{this.anchor.focus();
32064             }catch(e){}
32065         }else if(!Roo.isIE){
32066             try{
32067                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32068                 var l = noscroll.scrollLeft;
32069                 this.anchor.focus();
32070                 noscroll.scrollLeft = l;
32071             }catch(e){}
32072         }
32073     },
32074
32075     toggleCheck : function(value){
32076         var cb = this.checkbox;
32077         if(cb){
32078             cb.checked = (value === undefined ? !cb.checked : value);
32079         }
32080     },
32081
32082     blur : function(){
32083         try{
32084             this.anchor.blur();
32085         }catch(e){}
32086     },
32087
32088     animExpand : function(callback){
32089         var ct = Roo.get(this.ctNode);
32090         ct.stopFx();
32091         if(!this.node.hasChildNodes()){
32092             this.updateExpandIcon();
32093             this.ctNode.style.display = "";
32094             Roo.callback(callback);
32095             return;
32096         }
32097         this.animating = true;
32098         this.updateExpandIcon();
32099
32100         ct.slideIn('t', {
32101            callback : function(){
32102                this.animating = false;
32103                Roo.callback(callback);
32104             },
32105             scope: this,
32106             duration: this.node.ownerTree.duration || .25
32107         });
32108     },
32109
32110     highlight : function(){
32111         var tree = this.node.getOwnerTree();
32112         Roo.fly(this.wrap).highlight(
32113             tree.hlColor || "C3DAF9",
32114             {endColor: tree.hlBaseColor}
32115         );
32116     },
32117
32118     collapse : function(){
32119         this.updateExpandIcon();
32120         this.ctNode.style.display = "none";
32121     },
32122
32123     animCollapse : function(callback){
32124         var ct = Roo.get(this.ctNode);
32125         ct.enableDisplayMode('block');
32126         ct.stopFx();
32127
32128         this.animating = true;
32129         this.updateExpandIcon();
32130
32131         ct.slideOut('t', {
32132             callback : function(){
32133                this.animating = false;
32134                Roo.callback(callback);
32135             },
32136             scope: this,
32137             duration: this.node.ownerTree.duration || .25
32138         });
32139     },
32140
32141     getContainer : function(){
32142         return this.ctNode;
32143     },
32144
32145     getEl : function(){
32146         return this.wrap;
32147     },
32148
32149     appendDDGhost : function(ghostNode){
32150         ghostNode.appendChild(this.elNode.cloneNode(true));
32151     },
32152
32153     getDDRepairXY : function(){
32154         return Roo.lib.Dom.getXY(this.iconNode);
32155     },
32156
32157     onRender : function(){
32158         this.render();
32159     },
32160
32161     render : function(bulkRender){
32162         var n = this.node, a = n.attributes;
32163         var targetNode = n.parentNode ?
32164               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32165
32166         if(!this.rendered){
32167             this.rendered = true;
32168
32169             this.renderElements(n, a, targetNode, bulkRender);
32170
32171             if(a.qtip){
32172                if(this.textNode.setAttributeNS){
32173                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32174                    if(a.qtipTitle){
32175                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32176                    }
32177                }else{
32178                    this.textNode.setAttribute("ext:qtip", a.qtip);
32179                    if(a.qtipTitle){
32180                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32181                    }
32182                }
32183             }else if(a.qtipCfg){
32184                 a.qtipCfg.target = Roo.id(this.textNode);
32185                 Roo.QuickTips.register(a.qtipCfg);
32186             }
32187             this.initEvents();
32188             if(!this.node.expanded){
32189                 this.updateExpandIcon();
32190             }
32191         }else{
32192             if(bulkRender === true) {
32193                 targetNode.appendChild(this.wrap);
32194             }
32195         }
32196     },
32197
32198     renderElements : function(n, a, targetNode, bulkRender){
32199         // add some indent caching, this helps performance when rendering a large tree
32200         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32201         var t = n.getOwnerTree();
32202         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32203         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32204         var cb = typeof a.checked == 'boolean';
32205         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32206         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32207             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32208             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32209             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32210             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32211             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32212              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32213                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32214             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32215             "</li>"];
32216
32217         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32218             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32219                                 n.nextSibling.ui.getEl(), buf.join(""));
32220         }else{
32221             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32222         }
32223
32224         this.elNode = this.wrap.childNodes[0];
32225         this.ctNode = this.wrap.childNodes[1];
32226         var cs = this.elNode.childNodes;
32227         this.indentNode = cs[0];
32228         this.ecNode = cs[1];
32229         this.iconNode = cs[2];
32230         var index = 3;
32231         if(cb){
32232             this.checkbox = cs[3];
32233             index++;
32234         }
32235         this.anchor = cs[index];
32236         this.textNode = cs[index].firstChild;
32237     },
32238
32239     getAnchor : function(){
32240         return this.anchor;
32241     },
32242
32243     getTextEl : function(){
32244         return this.textNode;
32245     },
32246
32247     getIconEl : function(){
32248         return this.iconNode;
32249     },
32250
32251     isChecked : function(){
32252         return this.checkbox ? this.checkbox.checked : false;
32253     },
32254
32255     updateExpandIcon : function(){
32256         if(this.rendered){
32257             var n = this.node, c1, c2;
32258             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32259             var hasChild = n.hasChildNodes();
32260             if(hasChild){
32261                 if(n.expanded){
32262                     cls += "-minus";
32263                     c1 = "x-tree-node-collapsed";
32264                     c2 = "x-tree-node-expanded";
32265                 }else{
32266                     cls += "-plus";
32267                     c1 = "x-tree-node-expanded";
32268                     c2 = "x-tree-node-collapsed";
32269                 }
32270                 if(this.wasLeaf){
32271                     this.removeClass("x-tree-node-leaf");
32272                     this.wasLeaf = false;
32273                 }
32274                 if(this.c1 != c1 || this.c2 != c2){
32275                     Roo.fly(this.elNode).replaceClass(c1, c2);
32276                     this.c1 = c1; this.c2 = c2;
32277                 }
32278             }else{
32279                 if(!this.wasLeaf){
32280                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32281                     delete this.c1;
32282                     delete this.c2;
32283                     this.wasLeaf = true;
32284                 }
32285             }
32286             var ecc = "x-tree-ec-icon "+cls;
32287             if(this.ecc != ecc){
32288                 this.ecNode.className = ecc;
32289                 this.ecc = ecc;
32290             }
32291         }
32292     },
32293
32294     getChildIndent : function(){
32295         if(!this.childIndent){
32296             var buf = [];
32297             var p = this.node;
32298             while(p){
32299                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32300                     if(!p.isLast()) {
32301                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32302                     } else {
32303                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32304                     }
32305                 }
32306                 p = p.parentNode;
32307             }
32308             this.childIndent = buf.join("");
32309         }
32310         return this.childIndent;
32311     },
32312
32313     renderIndent : function(){
32314         if(this.rendered){
32315             var indent = "";
32316             var p = this.node.parentNode;
32317             if(p){
32318                 indent = p.ui.getChildIndent();
32319             }
32320             if(this.indentMarkup != indent){ // don't rerender if not required
32321                 this.indentNode.innerHTML = indent;
32322                 this.indentMarkup = indent;
32323             }
32324             this.updateExpandIcon();
32325         }
32326     }
32327 };
32328
32329 Roo.tree.RootTreeNodeUI = function(){
32330     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32331 };
32332 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32333     render : function(){
32334         if(!this.rendered){
32335             var targetNode = this.node.ownerTree.innerCt.dom;
32336             this.node.expanded = true;
32337             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32338             this.wrap = this.ctNode = targetNode.firstChild;
32339         }
32340     },
32341     collapse : function(){
32342     },
32343     expand : function(){
32344     }
32345 });/*
32346  * Based on:
32347  * Ext JS Library 1.1.1
32348  * Copyright(c) 2006-2007, Ext JS, LLC.
32349  *
32350  * Originally Released Under LGPL - original licence link has changed is not relivant.
32351  *
32352  * Fork - LGPL
32353  * <script type="text/javascript">
32354  */
32355 /**
32356  * @class Roo.tree.TreeLoader
32357  * @extends Roo.util.Observable
32358  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32359  * nodes from a specified URL. The response must be a javascript Array definition
32360  * who's elements are node definition objects. eg:
32361  * <pre><code>
32362    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32363     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32364 </code></pre>
32365  * <br><br>
32366  * A server request is sent, and child nodes are loaded only when a node is expanded.
32367  * The loading node's id is passed to the server under the parameter name "node" to
32368  * enable the server to produce the correct child nodes.
32369  * <br><br>
32370  * To pass extra parameters, an event handler may be attached to the "beforeload"
32371  * event, and the parameters specified in the TreeLoader's baseParams property:
32372  * <pre><code>
32373     myTreeLoader.on("beforeload", function(treeLoader, node) {
32374         this.baseParams.category = node.attributes.category;
32375     }, this);
32376 </code></pre><
32377  * This would pass an HTTP parameter called "category" to the server containing
32378  * the value of the Node's "category" attribute.
32379  * @constructor
32380  * Creates a new Treeloader.
32381  * @param {Object} config A config object containing config properties.
32382  */
32383 Roo.tree.TreeLoader = function(config){
32384     this.baseParams = {};
32385     this.requestMethod = "POST";
32386     Roo.apply(this, config);
32387
32388     this.addEvents({
32389     
32390         /**
32391          * @event beforeload
32392          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32393          * @param {Object} This TreeLoader object.
32394          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32395          * @param {Object} callback The callback function specified in the {@link #load} call.
32396          */
32397         beforeload : true,
32398         /**
32399          * @event load
32400          * Fires when the node has been successfuly loaded.
32401          * @param {Object} This TreeLoader object.
32402          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32403          * @param {Object} response The response object containing the data from the server.
32404          */
32405         load : true,
32406         /**
32407          * @event loadexception
32408          * Fires if the network request failed.
32409          * @param {Object} This TreeLoader object.
32410          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32411          * @param {Object} response The response object containing the data from the server.
32412          */
32413         loadexception : true,
32414         /**
32415          * @event create
32416          * Fires before a node is created, enabling you to return custom Node types 
32417          * @param {Object} This TreeLoader object.
32418          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32419          */
32420         create : true
32421     });
32422
32423     Roo.tree.TreeLoader.superclass.constructor.call(this);
32424 };
32425
32426 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32427     /**
32428     * @cfg {String} dataUrl The URL from which to request a Json string which
32429     * specifies an array of node definition object representing the child nodes
32430     * to be loaded.
32431     */
32432     /**
32433     * @cfg {Object} baseParams (optional) An object containing properties which
32434     * specify HTTP parameters to be passed to each request for child nodes.
32435     */
32436     /**
32437     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32438     * created by this loader. If the attributes sent by the server have an attribute in this object,
32439     * they take priority.
32440     */
32441     /**
32442     * @cfg {Object} uiProviders (optional) An object containing properties which
32443     * 
32444     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32445     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32446     * <i>uiProvider</i> attribute of a returned child node is a string rather
32447     * than a reference to a TreeNodeUI implementation, this that string value
32448     * is used as a property name in the uiProviders object. You can define the provider named
32449     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32450     */
32451     uiProviders : {},
32452
32453     /**
32454     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32455     * child nodes before loading.
32456     */
32457     clearOnLoad : true,
32458
32459     /**
32460     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32461     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32462     * Grid query { data : [ .....] }
32463     */
32464     
32465     root : false,
32466      /**
32467     * @cfg {String} queryParam (optional) 
32468     * Name of the query as it will be passed on the querystring (defaults to 'node')
32469     * eg. the request will be ?node=[id]
32470     */
32471     
32472     
32473     queryParam: false,
32474     
32475     /**
32476      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32477      * This is called automatically when a node is expanded, but may be used to reload
32478      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32479      * @param {Roo.tree.TreeNode} node
32480      * @param {Function} callback
32481      */
32482     load : function(node, callback){
32483         if(this.clearOnLoad){
32484             while(node.firstChild){
32485                 node.removeChild(node.firstChild);
32486             }
32487         }
32488         if(node.attributes.children){ // preloaded json children
32489             var cs = node.attributes.children;
32490             for(var i = 0, len = cs.length; i < len; i++){
32491                 node.appendChild(this.createNode(cs[i]));
32492             }
32493             if(typeof callback == "function"){
32494                 callback();
32495             }
32496         }else if(this.dataUrl){
32497             this.requestData(node, callback);
32498         }
32499     },
32500
32501     getParams: function(node){
32502         var buf = [], bp = this.baseParams;
32503         for(var key in bp){
32504             if(typeof bp[key] != "function"){
32505                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32506             }
32507         }
32508         var n = this.queryParam === false ? 'node' : this.queryParam;
32509         buf.push(n + "=", encodeURIComponent(node.id));
32510         return buf.join("");
32511     },
32512
32513     requestData : function(node, callback){
32514         if(this.fireEvent("beforeload", this, node, callback) !== false){
32515             this.transId = Roo.Ajax.request({
32516                 method:this.requestMethod,
32517                 url: this.dataUrl||this.url,
32518                 success: this.handleResponse,
32519                 failure: this.handleFailure,
32520                 scope: this,
32521                 argument: {callback: callback, node: node},
32522                 params: this.getParams(node)
32523             });
32524         }else{
32525             // if the load is cancelled, make sure we notify
32526             // the node that we are done
32527             if(typeof callback == "function"){
32528                 callback();
32529             }
32530         }
32531     },
32532
32533     isLoading : function(){
32534         return this.transId ? true : false;
32535     },
32536
32537     abort : function(){
32538         if(this.isLoading()){
32539             Roo.Ajax.abort(this.transId);
32540         }
32541     },
32542
32543     // private
32544     createNode : function(attr){
32545         // apply baseAttrs, nice idea Corey!
32546         if(this.baseAttrs){
32547             Roo.applyIf(attr, this.baseAttrs);
32548         }
32549         if(this.applyLoader !== false){
32550             attr.loader = this;
32551         }
32552         // uiProvider = depreciated..
32553         
32554         if(typeof(attr.uiProvider) == 'string'){
32555            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32556                 /**  eval:var:attr */ eval(attr.uiProvider);
32557         }
32558         if(typeof(this.uiProviders['default']) != 'undefined') {
32559             attr.uiProvider = this.uiProviders['default'];
32560         }
32561         
32562         this.fireEvent('create', this, attr);
32563         
32564         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32565         return(attr.leaf ?
32566                         new Roo.tree.TreeNode(attr) :
32567                         new Roo.tree.AsyncTreeNode(attr));
32568     },
32569
32570     processResponse : function(response, node, callback){
32571         var json = response.responseText;
32572         try {
32573             
32574             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32575             if (this.root !== false) {
32576                 o = o[this.root];
32577             }
32578             
32579             for(var i = 0, len = o.length; i < len; i++){
32580                 var n = this.createNode(o[i]);
32581                 if(n){
32582                     node.appendChild(n);
32583                 }
32584             }
32585             if(typeof callback == "function"){
32586                 callback(this, node);
32587             }
32588         }catch(e){
32589             this.handleFailure(response);
32590         }
32591     },
32592
32593     handleResponse : function(response){
32594         this.transId = false;
32595         var a = response.argument;
32596         this.processResponse(response, a.node, a.callback);
32597         this.fireEvent("load", this, a.node, response);
32598     },
32599
32600     handleFailure : function(response){
32601         this.transId = false;
32602         var a = response.argument;
32603         this.fireEvent("loadexception", this, a.node, response);
32604         if(typeof a.callback == "function"){
32605             a.callback(this, a.node);
32606         }
32607     }
32608 });/*
32609  * Based on:
32610  * Ext JS Library 1.1.1
32611  * Copyright(c) 2006-2007, Ext JS, LLC.
32612  *
32613  * Originally Released Under LGPL - original licence link has changed is not relivant.
32614  *
32615  * Fork - LGPL
32616  * <script type="text/javascript">
32617  */
32618
32619 /**
32620 * @class Roo.tree.TreeFilter
32621 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32622 * @param {TreePanel} tree
32623 * @param {Object} config (optional)
32624  */
32625 Roo.tree.TreeFilter = function(tree, config){
32626     this.tree = tree;
32627     this.filtered = {};
32628     Roo.apply(this, config);
32629 };
32630
32631 Roo.tree.TreeFilter.prototype = {
32632     clearBlank:false,
32633     reverse:false,
32634     autoClear:false,
32635     remove:false,
32636
32637      /**
32638      * Filter the data by a specific attribute.
32639      * @param {String/RegExp} value Either string that the attribute value
32640      * should start with or a RegExp to test against the attribute
32641      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32642      * @param {TreeNode} startNode (optional) The node to start the filter at.
32643      */
32644     filter : function(value, attr, startNode){
32645         attr = attr || "text";
32646         var f;
32647         if(typeof value == "string"){
32648             var vlen = value.length;
32649             // auto clear empty filter
32650             if(vlen == 0 && this.clearBlank){
32651                 this.clear();
32652                 return;
32653             }
32654             value = value.toLowerCase();
32655             f = function(n){
32656                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32657             };
32658         }else if(value.exec){ // regex?
32659             f = function(n){
32660                 return value.test(n.attributes[attr]);
32661             };
32662         }else{
32663             throw 'Illegal filter type, must be string or regex';
32664         }
32665         this.filterBy(f, null, startNode);
32666         },
32667
32668     /**
32669      * Filter by a function. The passed function will be called with each
32670      * node in the tree (or from the startNode). If the function returns true, the node is kept
32671      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32672      * @param {Function} fn The filter function
32673      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32674      */
32675     filterBy : function(fn, scope, startNode){
32676         startNode = startNode || this.tree.root;
32677         if(this.autoClear){
32678             this.clear();
32679         }
32680         var af = this.filtered, rv = this.reverse;
32681         var f = function(n){
32682             if(n == startNode){
32683                 return true;
32684             }
32685             if(af[n.id]){
32686                 return false;
32687             }
32688             var m = fn.call(scope || n, n);
32689             if(!m || rv){
32690                 af[n.id] = n;
32691                 n.ui.hide();
32692                 return false;
32693             }
32694             return true;
32695         };
32696         startNode.cascade(f);
32697         if(this.remove){
32698            for(var id in af){
32699                if(typeof id != "function"){
32700                    var n = af[id];
32701                    if(n && n.parentNode){
32702                        n.parentNode.removeChild(n);
32703                    }
32704                }
32705            }
32706         }
32707     },
32708
32709     /**
32710      * Clears the current filter. Note: with the "remove" option
32711      * set a filter cannot be cleared.
32712      */
32713     clear : function(){
32714         var t = this.tree;
32715         var af = this.filtered;
32716         for(var id in af){
32717             if(typeof id != "function"){
32718                 var n = af[id];
32719                 if(n){
32720                     n.ui.show();
32721                 }
32722             }
32723         }
32724         this.filtered = {};
32725     }
32726 };
32727 /*
32728  * Based on:
32729  * Ext JS Library 1.1.1
32730  * Copyright(c) 2006-2007, Ext JS, LLC.
32731  *
32732  * Originally Released Under LGPL - original licence link has changed is not relivant.
32733  *
32734  * Fork - LGPL
32735  * <script type="text/javascript">
32736  */
32737  
32738
32739 /**
32740  * @class Roo.tree.TreeSorter
32741  * Provides sorting of nodes in a TreePanel
32742  * 
32743  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32744  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32745  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32746  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32747  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32748  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32749  * @constructor
32750  * @param {TreePanel} tree
32751  * @param {Object} config
32752  */
32753 Roo.tree.TreeSorter = function(tree, config){
32754     Roo.apply(this, config);
32755     tree.on("beforechildrenrendered", this.doSort, this);
32756     tree.on("append", this.updateSort, this);
32757     tree.on("insert", this.updateSort, this);
32758     
32759     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32760     var p = this.property || "text";
32761     var sortType = this.sortType;
32762     var fs = this.folderSort;
32763     var cs = this.caseSensitive === true;
32764     var leafAttr = this.leafAttr || 'leaf';
32765
32766     this.sortFn = function(n1, n2){
32767         if(fs){
32768             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32769                 return 1;
32770             }
32771             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32772                 return -1;
32773             }
32774         }
32775         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32776         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32777         if(v1 < v2){
32778                         return dsc ? +1 : -1;
32779                 }else if(v1 > v2){
32780                         return dsc ? -1 : +1;
32781         }else{
32782                 return 0;
32783         }
32784     };
32785 };
32786
32787 Roo.tree.TreeSorter.prototype = {
32788     doSort : function(node){
32789         node.sort(this.sortFn);
32790     },
32791     
32792     compareNodes : function(n1, n2){
32793         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32794     },
32795     
32796     updateSort : function(tree, node){
32797         if(node.childrenRendered){
32798             this.doSort.defer(1, this, [node]);
32799         }
32800     }
32801 };/*
32802  * Based on:
32803  * Ext JS Library 1.1.1
32804  * Copyright(c) 2006-2007, Ext JS, LLC.
32805  *
32806  * Originally Released Under LGPL - original licence link has changed is not relivant.
32807  *
32808  * Fork - LGPL
32809  * <script type="text/javascript">
32810  */
32811
32812 if(Roo.dd.DropZone){
32813     
32814 Roo.tree.TreeDropZone = function(tree, config){
32815     this.allowParentInsert = false;
32816     this.allowContainerDrop = false;
32817     this.appendOnly = false;
32818     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32819     this.tree = tree;
32820     this.lastInsertClass = "x-tree-no-status";
32821     this.dragOverData = {};
32822 };
32823
32824 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32825     ddGroup : "TreeDD",
32826     
32827     expandDelay : 1000,
32828     
32829     expandNode : function(node){
32830         if(node.hasChildNodes() && !node.isExpanded()){
32831             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32832         }
32833     },
32834     
32835     queueExpand : function(node){
32836         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32837     },
32838     
32839     cancelExpand : function(){
32840         if(this.expandProcId){
32841             clearTimeout(this.expandProcId);
32842             this.expandProcId = false;
32843         }
32844     },
32845     
32846     isValidDropPoint : function(n, pt, dd, e, data){
32847         if(!n || !data){ return false; }
32848         var targetNode = n.node;
32849         var dropNode = data.node;
32850         // default drop rules
32851         if(!(targetNode && targetNode.isTarget && pt)){
32852             return false;
32853         }
32854         if(pt == "append" && targetNode.allowChildren === false){
32855             return false;
32856         }
32857         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32858             return false;
32859         }
32860         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32861             return false;
32862         }
32863         // reuse the object
32864         var overEvent = this.dragOverData;
32865         overEvent.tree = this.tree;
32866         overEvent.target = targetNode;
32867         overEvent.data = data;
32868         overEvent.point = pt;
32869         overEvent.source = dd;
32870         overEvent.rawEvent = e;
32871         overEvent.dropNode = dropNode;
32872         overEvent.cancel = false;  
32873         var result = this.tree.fireEvent("nodedragover", overEvent);
32874         return overEvent.cancel === false && result !== false;
32875     },
32876     
32877     getDropPoint : function(e, n, dd){
32878         var tn = n.node;
32879         if(tn.isRoot){
32880             return tn.allowChildren !== false ? "append" : false; // always append for root
32881         }
32882         var dragEl = n.ddel;
32883         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32884         var y = Roo.lib.Event.getPageY(e);
32885         //var noAppend = tn.allowChildren === false || tn.isLeaf();
32886         
32887         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
32888         var noAppend = tn.allowChildren === false;
32889         if(this.appendOnly || tn.parentNode.allowChildren === false){
32890             return noAppend ? false : "append";
32891         }
32892         var noBelow = false;
32893         if(!this.allowParentInsert){
32894             noBelow = tn.hasChildNodes() && tn.isExpanded();
32895         }
32896         var q = (b - t) / (noAppend ? 2 : 3);
32897         if(y >= t && y < (t + q)){
32898             return "above";
32899         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32900             return "below";
32901         }else{
32902             return "append";
32903         }
32904     },
32905     
32906     onNodeEnter : function(n, dd, e, data){
32907         this.cancelExpand();
32908     },
32909     
32910     onNodeOver : function(n, dd, e, data){
32911         var pt = this.getDropPoint(e, n, dd);
32912         var node = n.node;
32913         
32914         // auto node expand check
32915         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32916             this.queueExpand(node);
32917         }else if(pt != "append"){
32918             this.cancelExpand();
32919         }
32920         
32921         // set the insert point style on the target node
32922         var returnCls = this.dropNotAllowed;
32923         if(this.isValidDropPoint(n, pt, dd, e, data)){
32924            if(pt){
32925                var el = n.ddel;
32926                var cls;
32927                if(pt == "above"){
32928                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32929                    cls = "x-tree-drag-insert-above";
32930                }else if(pt == "below"){
32931                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32932                    cls = "x-tree-drag-insert-below";
32933                }else{
32934                    returnCls = "x-tree-drop-ok-append";
32935                    cls = "x-tree-drag-append";
32936                }
32937                if(this.lastInsertClass != cls){
32938                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32939                    this.lastInsertClass = cls;
32940                }
32941            }
32942        }
32943        return returnCls;
32944     },
32945     
32946     onNodeOut : function(n, dd, e, data){
32947         this.cancelExpand();
32948         this.removeDropIndicators(n);
32949     },
32950     
32951     onNodeDrop : function(n, dd, e, data){
32952         var point = this.getDropPoint(e, n, dd);
32953         var targetNode = n.node;
32954         targetNode.ui.startDrop();
32955         if(!this.isValidDropPoint(n, point, dd, e, data)){
32956             targetNode.ui.endDrop();
32957             return false;
32958         }
32959         // first try to find the drop node
32960         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32961         var dropEvent = {
32962             tree : this.tree,
32963             target: targetNode,
32964             data: data,
32965             point: point,
32966             source: dd,
32967             rawEvent: e,
32968             dropNode: dropNode,
32969             cancel: !dropNode   
32970         };
32971         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32972         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32973             targetNode.ui.endDrop();
32974             return false;
32975         }
32976         // allow target changing
32977         targetNode = dropEvent.target;
32978         if(point == "append" && !targetNode.isExpanded()){
32979             targetNode.expand(false, null, function(){
32980                 this.completeDrop(dropEvent);
32981             }.createDelegate(this));
32982         }else{
32983             this.completeDrop(dropEvent);
32984         }
32985         return true;
32986     },
32987     
32988     completeDrop : function(de){
32989         var ns = de.dropNode, p = de.point, t = de.target;
32990         if(!(ns instanceof Array)){
32991             ns = [ns];
32992         }
32993         var n;
32994         for(var i = 0, len = ns.length; i < len; i++){
32995             n = ns[i];
32996             if(p == "above"){
32997                 t.parentNode.insertBefore(n, t);
32998             }else if(p == "below"){
32999                 t.parentNode.insertBefore(n, t.nextSibling);
33000             }else{
33001                 t.appendChild(n);
33002             }
33003         }
33004         n.ui.focus();
33005         if(this.tree.hlDrop){
33006             n.ui.highlight();
33007         }
33008         t.ui.endDrop();
33009         this.tree.fireEvent("nodedrop", de);
33010     },
33011     
33012     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33013         if(this.tree.hlDrop){
33014             dropNode.ui.focus();
33015             dropNode.ui.highlight();
33016         }
33017         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33018     },
33019     
33020     getTree : function(){
33021         return this.tree;
33022     },
33023     
33024     removeDropIndicators : function(n){
33025         if(n && n.ddel){
33026             var el = n.ddel;
33027             Roo.fly(el).removeClass([
33028                     "x-tree-drag-insert-above",
33029                     "x-tree-drag-insert-below",
33030                     "x-tree-drag-append"]);
33031             this.lastInsertClass = "_noclass";
33032         }
33033     },
33034     
33035     beforeDragDrop : function(target, e, id){
33036         this.cancelExpand();
33037         return true;
33038     },
33039     
33040     afterRepair : function(data){
33041         if(data && Roo.enableFx){
33042             data.node.ui.highlight();
33043         }
33044         this.hideProxy();
33045     }    
33046 });
33047
33048 }
33049 /*
33050  * Based on:
33051  * Ext JS Library 1.1.1
33052  * Copyright(c) 2006-2007, Ext JS, LLC.
33053  *
33054  * Originally Released Under LGPL - original licence link has changed is not relivant.
33055  *
33056  * Fork - LGPL
33057  * <script type="text/javascript">
33058  */
33059  
33060
33061 if(Roo.dd.DragZone){
33062 Roo.tree.TreeDragZone = function(tree, config){
33063     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33064     this.tree = tree;
33065 };
33066
33067 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33068     ddGroup : "TreeDD",
33069     
33070     onBeforeDrag : function(data, e){
33071         var n = data.node;
33072         return n && n.draggable && !n.disabled;
33073     },
33074     
33075     onInitDrag : function(e){
33076         var data = this.dragData;
33077         this.tree.getSelectionModel().select(data.node);
33078         this.proxy.update("");
33079         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33080         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33081     },
33082     
33083     getRepairXY : function(e, data){
33084         return data.node.ui.getDDRepairXY();
33085     },
33086     
33087     onEndDrag : function(data, e){
33088         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33089     },
33090     
33091     onValidDrop : function(dd, e, id){
33092         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33093         this.hideProxy();
33094     },
33095     
33096     beforeInvalidDrop : function(e, id){
33097         // this scrolls the original position back into view
33098         var sm = this.tree.getSelectionModel();
33099         sm.clearSelections();
33100         sm.select(this.dragData.node);
33101     }
33102 });
33103 }/*
33104  * Based on:
33105  * Ext JS Library 1.1.1
33106  * Copyright(c) 2006-2007, Ext JS, LLC.
33107  *
33108  * Originally Released Under LGPL - original licence link has changed is not relivant.
33109  *
33110  * Fork - LGPL
33111  * <script type="text/javascript">
33112  */
33113 /**
33114  * @class Roo.tree.TreeEditor
33115  * @extends Roo.Editor
33116  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33117  * as the editor field.
33118  * @constructor
33119  * @param {TreePanel} tree
33120  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33121  */
33122 Roo.tree.TreeEditor = function(tree, config){
33123     config = config || {};
33124     var field = config.events ? config : new Roo.form.TextField(config);
33125     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33126
33127     this.tree = tree;
33128
33129     tree.on('beforeclick', this.beforeNodeClick, this);
33130     tree.getTreeEl().on('mousedown', this.hide, this);
33131     this.on('complete', this.updateNode, this);
33132     this.on('beforestartedit', this.fitToTree, this);
33133     this.on('startedit', this.bindScroll, this, {delay:10});
33134     this.on('specialkey', this.onSpecialKey, this);
33135 };
33136
33137 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33138     /**
33139      * @cfg {String} alignment
33140      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33141      */
33142     alignment: "l-l",
33143     // inherit
33144     autoSize: false,
33145     /**
33146      * @cfg {Boolean} hideEl
33147      * True to hide the bound element while the editor is displayed (defaults to false)
33148      */
33149     hideEl : false,
33150     /**
33151      * @cfg {String} cls
33152      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33153      */
33154     cls: "x-small-editor x-tree-editor",
33155     /**
33156      * @cfg {Boolean} shim
33157      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33158      */
33159     shim:false,
33160     // inherit
33161     shadow:"frame",
33162     /**
33163      * @cfg {Number} maxWidth
33164      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33165      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33166      * scroll and client offsets into account prior to each edit.
33167      */
33168     maxWidth: 250,
33169
33170     editDelay : 350,
33171
33172     // private
33173     fitToTree : function(ed, el){
33174         var td = this.tree.getTreeEl().dom, nd = el.dom;
33175         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33176             td.scrollLeft = nd.offsetLeft;
33177         }
33178         var w = Math.min(
33179                 this.maxWidth,
33180                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33181         this.setSize(w, '');
33182     },
33183
33184     // private
33185     triggerEdit : function(node){
33186         this.completeEdit();
33187         this.editNode = node;
33188         this.startEdit(node.ui.textNode, node.text);
33189     },
33190
33191     // private
33192     bindScroll : function(){
33193         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33194     },
33195
33196     // private
33197     beforeNodeClick : function(node, e){
33198         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33199         this.lastClick = new Date();
33200         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33201             e.stopEvent();
33202             this.triggerEdit(node);
33203             return false;
33204         }
33205     },
33206
33207     // private
33208     updateNode : function(ed, value){
33209         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33210         this.editNode.setText(value);
33211     },
33212
33213     // private
33214     onHide : function(){
33215         Roo.tree.TreeEditor.superclass.onHide.call(this);
33216         if(this.editNode){
33217             this.editNode.ui.focus();
33218         }
33219     },
33220
33221     // private
33222     onSpecialKey : function(field, e){
33223         var k = e.getKey();
33224         if(k == e.ESC){
33225             e.stopEvent();
33226             this.cancelEdit();
33227         }else if(k == e.ENTER && !e.hasModifier()){
33228             e.stopEvent();
33229             this.completeEdit();
33230         }
33231     }
33232 });//<Script type="text/javascript">
33233 /*
33234  * Based on:
33235  * Ext JS Library 1.1.1
33236  * Copyright(c) 2006-2007, Ext JS, LLC.
33237  *
33238  * Originally Released Under LGPL - original licence link has changed is not relivant.
33239  *
33240  * Fork - LGPL
33241  * <script type="text/javascript">
33242  */
33243  
33244 /**
33245  * Not documented??? - probably should be...
33246  */
33247
33248 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33249     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33250     
33251     renderElements : function(n, a, targetNode, bulkRender){
33252         //consel.log("renderElements?");
33253         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33254
33255         var t = n.getOwnerTree();
33256         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33257         
33258         var cols = t.columns;
33259         var bw = t.borderWidth;
33260         var c = cols[0];
33261         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33262          var cb = typeof a.checked == "boolean";
33263         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33264         var colcls = 'x-t-' + tid + '-c0';
33265         var buf = [
33266             '<li class="x-tree-node">',
33267             
33268                 
33269                 '<div class="x-tree-node-el ', a.cls,'">',
33270                     // extran...
33271                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33272                 
33273                 
33274                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33275                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33276                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33277                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33278                            (a.iconCls ? ' '+a.iconCls : ''),
33279                            '" unselectable="on" />',
33280                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33281                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33282                              
33283                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33284                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33285                             '<span unselectable="on" qtip="' + tx + '">',
33286                              tx,
33287                              '</span></a>' ,
33288                     '</div>',
33289                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33290                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33291                  ];
33292         for(var i = 1, len = cols.length; i < len; i++){
33293             c = cols[i];
33294             colcls = 'x-t-' + tid + '-c' +i;
33295             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33296             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33297                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33298                       "</div>");
33299          }
33300          
33301          buf.push(
33302             '</a>',
33303             '<div class="x-clear"></div></div>',
33304             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33305             "</li>");
33306         
33307         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33308             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33309                                 n.nextSibling.ui.getEl(), buf.join(""));
33310         }else{
33311             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33312         }
33313         var el = this.wrap.firstChild;
33314         this.elRow = el;
33315         this.elNode = el.firstChild;
33316         this.ranchor = el.childNodes[1];
33317         this.ctNode = this.wrap.childNodes[1];
33318         var cs = el.firstChild.childNodes;
33319         this.indentNode = cs[0];
33320         this.ecNode = cs[1];
33321         this.iconNode = cs[2];
33322         var index = 3;
33323         if(cb){
33324             this.checkbox = cs[3];
33325             index++;
33326         }
33327         this.anchor = cs[index];
33328         
33329         this.textNode = cs[index].firstChild;
33330         
33331         //el.on("click", this.onClick, this);
33332         //el.on("dblclick", this.onDblClick, this);
33333         
33334         
33335        // console.log(this);
33336     },
33337     initEvents : function(){
33338         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33339         
33340             
33341         var a = this.ranchor;
33342
33343         var el = Roo.get(a);
33344
33345         if(Roo.isOpera){ // opera render bug ignores the CSS
33346             el.setStyle("text-decoration", "none");
33347         }
33348
33349         el.on("click", this.onClick, this);
33350         el.on("dblclick", this.onDblClick, this);
33351         el.on("contextmenu", this.onContextMenu, this);
33352         
33353     },
33354     
33355     /*onSelectedChange : function(state){
33356         if(state){
33357             this.focus();
33358             this.addClass("x-tree-selected");
33359         }else{
33360             //this.blur();
33361             this.removeClass("x-tree-selected");
33362         }
33363     },*/
33364     addClass : function(cls){
33365         if(this.elRow){
33366             Roo.fly(this.elRow).addClass(cls);
33367         }
33368         
33369     },
33370     
33371     
33372     removeClass : function(cls){
33373         if(this.elRow){
33374             Roo.fly(this.elRow).removeClass(cls);
33375         }
33376     }
33377
33378     
33379     
33380 });//<Script type="text/javascript">
33381
33382 /*
33383  * Based on:
33384  * Ext JS Library 1.1.1
33385  * Copyright(c) 2006-2007, Ext JS, LLC.
33386  *
33387  * Originally Released Under LGPL - original licence link has changed is not relivant.
33388  *
33389  * Fork - LGPL
33390  * <script type="text/javascript">
33391  */
33392  
33393
33394 /**
33395  * @class Roo.tree.ColumnTree
33396  * @extends Roo.data.TreePanel
33397  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33398  * @cfg {int} borderWidth  compined right/left border allowance
33399  * @constructor
33400  * @param {String/HTMLElement/Element} el The container element
33401  * @param {Object} config
33402  */
33403 Roo.tree.ColumnTree =  function(el, config)
33404 {
33405    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33406    this.addEvents({
33407         /**
33408         * @event resize
33409         * Fire this event on a container when it resizes
33410         * @param {int} w Width
33411         * @param {int} h Height
33412         */
33413        "resize" : true
33414     });
33415     this.on('resize', this.onResize, this);
33416 };
33417
33418 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33419     //lines:false,
33420     
33421     
33422     borderWidth: Roo.isBorderBox ? 0 : 2, 
33423     headEls : false,
33424     
33425     render : function(){
33426         // add the header.....
33427        
33428         Roo.tree.ColumnTree.superclass.render.apply(this);
33429         
33430         this.el.addClass('x-column-tree');
33431         
33432         this.headers = this.el.createChild(
33433             {cls:'x-tree-headers'},this.innerCt.dom);
33434    
33435         var cols = this.columns, c;
33436         var totalWidth = 0;
33437         this.headEls = [];
33438         var  len = cols.length;
33439         for(var i = 0; i < len; i++){
33440              c = cols[i];
33441              totalWidth += c.width;
33442             this.headEls.push(this.headers.createChild({
33443                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33444                  cn: {
33445                      cls:'x-tree-hd-text',
33446                      html: c.header
33447                  },
33448                  style:'width:'+(c.width-this.borderWidth)+'px;'
33449              }));
33450         }
33451         this.headers.createChild({cls:'x-clear'});
33452         // prevent floats from wrapping when clipped
33453         this.headers.setWidth(totalWidth);
33454         //this.innerCt.setWidth(totalWidth);
33455         this.innerCt.setStyle({ overflow: 'auto' });
33456         this.onResize(this.width, this.height);
33457              
33458         
33459     },
33460     onResize : function(w,h)
33461     {
33462         this.height = h;
33463         this.width = w;
33464         // resize cols..
33465         this.innerCt.setWidth(this.width);
33466         this.innerCt.setHeight(this.height-20);
33467         
33468         // headers...
33469         var cols = this.columns, c;
33470         var totalWidth = 0;
33471         var expEl = false;
33472         var len = cols.length;
33473         for(var i = 0; i < len; i++){
33474             c = cols[i];
33475             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33476                 // it's the expander..
33477                 expEl  = this.headEls[i];
33478                 continue;
33479             }
33480             totalWidth += c.width;
33481             
33482         }
33483         if (expEl) {
33484             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33485         }
33486         this.headers.setWidth(w-20);
33487
33488         
33489         
33490         
33491     }
33492 });
33493 /*
33494  * Based on:
33495  * Ext JS Library 1.1.1
33496  * Copyright(c) 2006-2007, Ext JS, LLC.
33497  *
33498  * Originally Released Under LGPL - original licence link has changed is not relivant.
33499  *
33500  * Fork - LGPL
33501  * <script type="text/javascript">
33502  */
33503  
33504 /**
33505  * @class Roo.menu.Menu
33506  * @extends Roo.util.Observable
33507  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33508  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33509  * @constructor
33510  * Creates a new Menu
33511  * @param {Object} config Configuration options
33512  */
33513 Roo.menu.Menu = function(config){
33514     Roo.apply(this, config);
33515     this.id = this.id || Roo.id();
33516     this.addEvents({
33517         /**
33518          * @event beforeshow
33519          * Fires before this menu is displayed
33520          * @param {Roo.menu.Menu} this
33521          */
33522         beforeshow : true,
33523         /**
33524          * @event beforehide
33525          * Fires before this menu is hidden
33526          * @param {Roo.menu.Menu} this
33527          */
33528         beforehide : true,
33529         /**
33530          * @event show
33531          * Fires after this menu is displayed
33532          * @param {Roo.menu.Menu} this
33533          */
33534         show : true,
33535         /**
33536          * @event hide
33537          * Fires after this menu is hidden
33538          * @param {Roo.menu.Menu} this
33539          */
33540         hide : true,
33541         /**
33542          * @event click
33543          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33544          * @param {Roo.menu.Menu} this
33545          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33546          * @param {Roo.EventObject} e
33547          */
33548         click : true,
33549         /**
33550          * @event mouseover
33551          * Fires when the mouse is hovering over this menu
33552          * @param {Roo.menu.Menu} this
33553          * @param {Roo.EventObject} e
33554          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33555          */
33556         mouseover : true,
33557         /**
33558          * @event mouseout
33559          * Fires when the mouse exits this menu
33560          * @param {Roo.menu.Menu} this
33561          * @param {Roo.EventObject} e
33562          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33563          */
33564         mouseout : true,
33565         /**
33566          * @event itemclick
33567          * Fires when a menu item contained in this menu is clicked
33568          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33569          * @param {Roo.EventObject} e
33570          */
33571         itemclick: true
33572     });
33573     if (this.registerMenu) {
33574         Roo.menu.MenuMgr.register(this);
33575     }
33576     
33577     var mis = this.items;
33578     this.items = new Roo.util.MixedCollection();
33579     if(mis){
33580         this.add.apply(this, mis);
33581     }
33582 };
33583
33584 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33585     /**
33586      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33587      */
33588     minWidth : 120,
33589     /**
33590      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33591      * for bottom-right shadow (defaults to "sides")
33592      */
33593     shadow : "sides",
33594     /**
33595      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33596      * this menu (defaults to "tl-tr?")
33597      */
33598     subMenuAlign : "tl-tr?",
33599     /**
33600      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33601      * relative to its element of origin (defaults to "tl-bl?")
33602      */
33603     defaultAlign : "tl-bl?",
33604     /**
33605      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33606      */
33607     allowOtherMenus : false,
33608     /**
33609      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33610      */
33611     registerMenu : true,
33612
33613     hidden:true,
33614
33615     // private
33616     render : function(){
33617         if(this.el){
33618             return;
33619         }
33620         var el = this.el = new Roo.Layer({
33621             cls: "x-menu",
33622             shadow:this.shadow,
33623             constrain: false,
33624             parentEl: this.parentEl || document.body,
33625             zindex:15000
33626         });
33627
33628         this.keyNav = new Roo.menu.MenuNav(this);
33629
33630         if(this.plain){
33631             el.addClass("x-menu-plain");
33632         }
33633         if(this.cls){
33634             el.addClass(this.cls);
33635         }
33636         // generic focus element
33637         this.focusEl = el.createChild({
33638             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33639         });
33640         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33641         ul.on("click", this.onClick, this);
33642         ul.on("mouseover", this.onMouseOver, this);
33643         ul.on("mouseout", this.onMouseOut, this);
33644         this.items.each(function(item){
33645             var li = document.createElement("li");
33646             li.className = "x-menu-list-item";
33647             ul.dom.appendChild(li);
33648             item.render(li, this);
33649         }, this);
33650         this.ul = ul;
33651         this.autoWidth();
33652     },
33653
33654     // private
33655     autoWidth : function(){
33656         var el = this.el, ul = this.ul;
33657         if(!el){
33658             return;
33659         }
33660         var w = this.width;
33661         if(w){
33662             el.setWidth(w);
33663         }else if(Roo.isIE){
33664             el.setWidth(this.minWidth);
33665             var t = el.dom.offsetWidth; // force recalc
33666             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33667         }
33668     },
33669
33670     // private
33671     delayAutoWidth : function(){
33672         if(this.rendered){
33673             if(!this.awTask){
33674                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33675             }
33676             this.awTask.delay(20);
33677         }
33678     },
33679
33680     // private
33681     findTargetItem : function(e){
33682         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33683         if(t && t.menuItemId){
33684             return this.items.get(t.menuItemId);
33685         }
33686     },
33687
33688     // private
33689     onClick : function(e){
33690         var t;
33691         if(t = this.findTargetItem(e)){
33692             t.onClick(e);
33693             this.fireEvent("click", this, t, e);
33694         }
33695     },
33696
33697     // private
33698     setActiveItem : function(item, autoExpand){
33699         if(item != this.activeItem){
33700             if(this.activeItem){
33701                 this.activeItem.deactivate();
33702             }
33703             this.activeItem = item;
33704             item.activate(autoExpand);
33705         }else if(autoExpand){
33706             item.expandMenu();
33707         }
33708     },
33709
33710     // private
33711     tryActivate : function(start, step){
33712         var items = this.items;
33713         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33714             var item = items.get(i);
33715             if(!item.disabled && item.canActivate){
33716                 this.setActiveItem(item, false);
33717                 return item;
33718             }
33719         }
33720         return false;
33721     },
33722
33723     // private
33724     onMouseOver : function(e){
33725         var t;
33726         if(t = this.findTargetItem(e)){
33727             if(t.canActivate && !t.disabled){
33728                 this.setActiveItem(t, true);
33729             }
33730         }
33731         this.fireEvent("mouseover", this, e, t);
33732     },
33733
33734     // private
33735     onMouseOut : function(e){
33736         var t;
33737         if(t = this.findTargetItem(e)){
33738             if(t == this.activeItem && t.shouldDeactivate(e)){
33739                 this.activeItem.deactivate();
33740                 delete this.activeItem;
33741             }
33742         }
33743         this.fireEvent("mouseout", this, e, t);
33744     },
33745
33746     /**
33747      * Read-only.  Returns true if the menu is currently displayed, else false.
33748      * @type Boolean
33749      */
33750     isVisible : function(){
33751         return this.el && !this.hidden;
33752     },
33753
33754     /**
33755      * Displays this menu relative to another element
33756      * @param {String/HTMLElement/Roo.Element} element The element to align to
33757      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33758      * the element (defaults to this.defaultAlign)
33759      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33760      */
33761     show : function(el, pos, parentMenu){
33762         this.parentMenu = parentMenu;
33763         if(!this.el){
33764             this.render();
33765         }
33766         this.fireEvent("beforeshow", this);
33767         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33768     },
33769
33770     /**
33771      * Displays this menu at a specific xy position
33772      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33773      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33774      */
33775     showAt : function(xy, parentMenu, /* private: */_e){
33776         this.parentMenu = parentMenu;
33777         if(!this.el){
33778             this.render();
33779         }
33780         if(_e !== false){
33781             this.fireEvent("beforeshow", this);
33782             xy = this.el.adjustForConstraints(xy);
33783         }
33784         this.el.setXY(xy);
33785         this.el.show();
33786         this.hidden = false;
33787         this.focus();
33788         this.fireEvent("show", this);
33789     },
33790
33791     focus : function(){
33792         if(!this.hidden){
33793             this.doFocus.defer(50, this);
33794         }
33795     },
33796
33797     doFocus : function(){
33798         if(!this.hidden){
33799             this.focusEl.focus();
33800         }
33801     },
33802
33803     /**
33804      * Hides this menu and optionally all parent menus
33805      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33806      */
33807     hide : function(deep){
33808         if(this.el && this.isVisible()){
33809             this.fireEvent("beforehide", this);
33810             if(this.activeItem){
33811                 this.activeItem.deactivate();
33812                 this.activeItem = null;
33813             }
33814             this.el.hide();
33815             this.hidden = true;
33816             this.fireEvent("hide", this);
33817         }
33818         if(deep === true && this.parentMenu){
33819             this.parentMenu.hide(true);
33820         }
33821     },
33822
33823     /**
33824      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33825      * Any of the following are valid:
33826      * <ul>
33827      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33828      * <li>An HTMLElement object which will be converted to a menu item</li>
33829      * <li>A menu item config object that will be created as a new menu item</li>
33830      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33831      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33832      * </ul>
33833      * Usage:
33834      * <pre><code>
33835 // Create the menu
33836 var menu = new Roo.menu.Menu();
33837
33838 // Create a menu item to add by reference
33839 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33840
33841 // Add a bunch of items at once using different methods.
33842 // Only the last item added will be returned.
33843 var item = menu.add(
33844     menuItem,                // add existing item by ref
33845     'Dynamic Item',          // new TextItem
33846     '-',                     // new separator
33847     { text: 'Config Item' }  // new item by config
33848 );
33849 </code></pre>
33850      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33851      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33852      */
33853     add : function(){
33854         var a = arguments, l = a.length, item;
33855         for(var i = 0; i < l; i++){
33856             var el = a[i];
33857             if ((typeof(el) == "object") && el.xtype && el.xns) {
33858                 el = Roo.factory(el, Roo.menu);
33859             }
33860             
33861             if(el.render){ // some kind of Item
33862                 item = this.addItem(el);
33863             }else if(typeof el == "string"){ // string
33864                 if(el == "separator" || el == "-"){
33865                     item = this.addSeparator();
33866                 }else{
33867                     item = this.addText(el);
33868                 }
33869             }else if(el.tagName || el.el){ // element
33870                 item = this.addElement(el);
33871             }else if(typeof el == "object"){ // must be menu item config?
33872                 item = this.addMenuItem(el);
33873             }
33874         }
33875         return item;
33876     },
33877
33878     /**
33879      * Returns this menu's underlying {@link Roo.Element} object
33880      * @return {Roo.Element} The element
33881      */
33882     getEl : function(){
33883         if(!this.el){
33884             this.render();
33885         }
33886         return this.el;
33887     },
33888
33889     /**
33890      * Adds a separator bar to the menu
33891      * @return {Roo.menu.Item} The menu item that was added
33892      */
33893     addSeparator : function(){
33894         return this.addItem(new Roo.menu.Separator());
33895     },
33896
33897     /**
33898      * Adds an {@link Roo.Element} object to the menu
33899      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33900      * @return {Roo.menu.Item} The menu item that was added
33901      */
33902     addElement : function(el){
33903         return this.addItem(new Roo.menu.BaseItem(el));
33904     },
33905
33906     /**
33907      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33908      * @param {Roo.menu.Item} item The menu item to add
33909      * @return {Roo.menu.Item} The menu item that was added
33910      */
33911     addItem : function(item){
33912         this.items.add(item);
33913         if(this.ul){
33914             var li = document.createElement("li");
33915             li.className = "x-menu-list-item";
33916             this.ul.dom.appendChild(li);
33917             item.render(li, this);
33918             this.delayAutoWidth();
33919         }
33920         return item;
33921     },
33922
33923     /**
33924      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33925      * @param {Object} config A MenuItem config object
33926      * @return {Roo.menu.Item} The menu item that was added
33927      */
33928     addMenuItem : function(config){
33929         if(!(config instanceof Roo.menu.Item)){
33930             if(typeof config.checked == "boolean"){ // must be check menu item config?
33931                 config = new Roo.menu.CheckItem(config);
33932             }else{
33933                 config = new Roo.menu.Item(config);
33934             }
33935         }
33936         return this.addItem(config);
33937     },
33938
33939     /**
33940      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33941      * @param {String} text The text to display in the menu item
33942      * @return {Roo.menu.Item} The menu item that was added
33943      */
33944     addText : function(text){
33945         return this.addItem(new Roo.menu.TextItem({ text : text }));
33946     },
33947
33948     /**
33949      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33950      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33951      * @param {Roo.menu.Item} item The menu item to add
33952      * @return {Roo.menu.Item} The menu item that was added
33953      */
33954     insert : function(index, item){
33955         this.items.insert(index, item);
33956         if(this.ul){
33957             var li = document.createElement("li");
33958             li.className = "x-menu-list-item";
33959             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33960             item.render(li, this);
33961             this.delayAutoWidth();
33962         }
33963         return item;
33964     },
33965
33966     /**
33967      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33968      * @param {Roo.menu.Item} item The menu item to remove
33969      */
33970     remove : function(item){
33971         this.items.removeKey(item.id);
33972         item.destroy();
33973     },
33974
33975     /**
33976      * Removes and destroys all items in the menu
33977      */
33978     removeAll : function(){
33979         var f;
33980         while(f = this.items.first()){
33981             this.remove(f);
33982         }
33983     }
33984 });
33985
33986 // MenuNav is a private utility class used internally by the Menu
33987 Roo.menu.MenuNav = function(menu){
33988     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33989     this.scope = this.menu = menu;
33990 };
33991
33992 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33993     doRelay : function(e, h){
33994         var k = e.getKey();
33995         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33996             this.menu.tryActivate(0, 1);
33997             return false;
33998         }
33999         return h.call(this.scope || this, e, this.menu);
34000     },
34001
34002     up : function(e, m){
34003         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34004             m.tryActivate(m.items.length-1, -1);
34005         }
34006     },
34007
34008     down : function(e, m){
34009         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34010             m.tryActivate(0, 1);
34011         }
34012     },
34013
34014     right : function(e, m){
34015         if(m.activeItem){
34016             m.activeItem.expandMenu(true);
34017         }
34018     },
34019
34020     left : function(e, m){
34021         m.hide();
34022         if(m.parentMenu && m.parentMenu.activeItem){
34023             m.parentMenu.activeItem.activate();
34024         }
34025     },
34026
34027     enter : function(e, m){
34028         if(m.activeItem){
34029             e.stopPropagation();
34030             m.activeItem.onClick(e);
34031             m.fireEvent("click", this, m.activeItem);
34032             return true;
34033         }
34034     }
34035 });/*
34036  * Based on:
34037  * Ext JS Library 1.1.1
34038  * Copyright(c) 2006-2007, Ext JS, LLC.
34039  *
34040  * Originally Released Under LGPL - original licence link has changed is not relivant.
34041  *
34042  * Fork - LGPL
34043  * <script type="text/javascript">
34044  */
34045  
34046 /**
34047  * @class Roo.menu.MenuMgr
34048  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34049  * @singleton
34050  */
34051 Roo.menu.MenuMgr = function(){
34052    var menus, active, groups = {}, attached = false, lastShow = new Date();
34053
34054    // private - called when first menu is created
34055    function init(){
34056        menus = {};
34057        active = new Roo.util.MixedCollection();
34058        Roo.get(document).addKeyListener(27, function(){
34059            if(active.length > 0){
34060                hideAll();
34061            }
34062        });
34063    }
34064
34065    // private
34066    function hideAll(){
34067        if(active && active.length > 0){
34068            var c = active.clone();
34069            c.each(function(m){
34070                m.hide();
34071            });
34072        }
34073    }
34074
34075    // private
34076    function onHide(m){
34077        active.remove(m);
34078        if(active.length < 1){
34079            Roo.get(document).un("mousedown", onMouseDown);
34080            attached = false;
34081        }
34082    }
34083
34084    // private
34085    function onShow(m){
34086        var last = active.last();
34087        lastShow = new Date();
34088        active.add(m);
34089        if(!attached){
34090            Roo.get(document).on("mousedown", onMouseDown);
34091            attached = true;
34092        }
34093        if(m.parentMenu){
34094           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34095           m.parentMenu.activeChild = m;
34096        }else if(last && last.isVisible()){
34097           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34098        }
34099    }
34100
34101    // private
34102    function onBeforeHide(m){
34103        if(m.activeChild){
34104            m.activeChild.hide();
34105        }
34106        if(m.autoHideTimer){
34107            clearTimeout(m.autoHideTimer);
34108            delete m.autoHideTimer;
34109        }
34110    }
34111
34112    // private
34113    function onBeforeShow(m){
34114        var pm = m.parentMenu;
34115        if(!pm && !m.allowOtherMenus){
34116            hideAll();
34117        }else if(pm && pm.activeChild && active != m){
34118            pm.activeChild.hide();
34119        }
34120    }
34121
34122    // private
34123    function onMouseDown(e){
34124        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34125            hideAll();
34126        }
34127    }
34128
34129    // private
34130    function onBeforeCheck(mi, state){
34131        if(state){
34132            var g = groups[mi.group];
34133            for(var i = 0, l = g.length; i < l; i++){
34134                if(g[i] != mi){
34135                    g[i].setChecked(false);
34136                }
34137            }
34138        }
34139    }
34140
34141    return {
34142
34143        /**
34144         * Hides all menus that are currently visible
34145         */
34146        hideAll : function(){
34147             hideAll();  
34148        },
34149
34150        // private
34151        register : function(menu){
34152            if(!menus){
34153                init();
34154            }
34155            menus[menu.id] = menu;
34156            menu.on("beforehide", onBeforeHide);
34157            menu.on("hide", onHide);
34158            menu.on("beforeshow", onBeforeShow);
34159            menu.on("show", onShow);
34160            var g = menu.group;
34161            if(g && menu.events["checkchange"]){
34162                if(!groups[g]){
34163                    groups[g] = [];
34164                }
34165                groups[g].push(menu);
34166                menu.on("checkchange", onCheck);
34167            }
34168        },
34169
34170         /**
34171          * Returns a {@link Roo.menu.Menu} object
34172          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34173          * be used to generate and return a new Menu instance.
34174          */
34175        get : function(menu){
34176            if(typeof menu == "string"){ // menu id
34177                return menus[menu];
34178            }else if(menu.events){  // menu instance
34179                return menu;
34180            }else if(typeof menu.length == 'number'){ // array of menu items?
34181                return new Roo.menu.Menu({items:menu});
34182            }else{ // otherwise, must be a config
34183                return new Roo.menu.Menu(menu);
34184            }
34185        },
34186
34187        // private
34188        unregister : function(menu){
34189            delete menus[menu.id];
34190            menu.un("beforehide", onBeforeHide);
34191            menu.un("hide", onHide);
34192            menu.un("beforeshow", onBeforeShow);
34193            menu.un("show", onShow);
34194            var g = menu.group;
34195            if(g && menu.events["checkchange"]){
34196                groups[g].remove(menu);
34197                menu.un("checkchange", onCheck);
34198            }
34199        },
34200
34201        // private
34202        registerCheckable : function(menuItem){
34203            var g = menuItem.group;
34204            if(g){
34205                if(!groups[g]){
34206                    groups[g] = [];
34207                }
34208                groups[g].push(menuItem);
34209                menuItem.on("beforecheckchange", onBeforeCheck);
34210            }
34211        },
34212
34213        // private
34214        unregisterCheckable : function(menuItem){
34215            var g = menuItem.group;
34216            if(g){
34217                groups[g].remove(menuItem);
34218                menuItem.un("beforecheckchange", onBeforeCheck);
34219            }
34220        }
34221    };
34222 }();/*
34223  * Based on:
34224  * Ext JS Library 1.1.1
34225  * Copyright(c) 2006-2007, Ext JS, LLC.
34226  *
34227  * Originally Released Under LGPL - original licence link has changed is not relivant.
34228  *
34229  * Fork - LGPL
34230  * <script type="text/javascript">
34231  */
34232  
34233
34234 /**
34235  * @class Roo.menu.BaseItem
34236  * @extends Roo.Component
34237  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34238  * management and base configuration options shared by all menu components.
34239  * @constructor
34240  * Creates a new BaseItem
34241  * @param {Object} config Configuration options
34242  */
34243 Roo.menu.BaseItem = function(config){
34244     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34245
34246     this.addEvents({
34247         /**
34248          * @event click
34249          * Fires when this item is clicked
34250          * @param {Roo.menu.BaseItem} this
34251          * @param {Roo.EventObject} e
34252          */
34253         click: true,
34254         /**
34255          * @event activate
34256          * Fires when this item is activated
34257          * @param {Roo.menu.BaseItem} this
34258          */
34259         activate : true,
34260         /**
34261          * @event deactivate
34262          * Fires when this item is deactivated
34263          * @param {Roo.menu.BaseItem} this
34264          */
34265         deactivate : true
34266     });
34267
34268     if(this.handler){
34269         this.on("click", this.handler, this.scope, true);
34270     }
34271 };
34272
34273 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34274     /**
34275      * @cfg {Function} handler
34276      * A function that will handle the click event of this menu item (defaults to undefined)
34277      */
34278     /**
34279      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34280      */
34281     canActivate : false,
34282     /**
34283      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34284      */
34285     activeClass : "x-menu-item-active",
34286     /**
34287      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34288      */
34289     hideOnClick : true,
34290     /**
34291      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34292      */
34293     hideDelay : 100,
34294
34295     // private
34296     ctype: "Roo.menu.BaseItem",
34297
34298     // private
34299     actionMode : "container",
34300
34301     // private
34302     render : function(container, parentMenu){
34303         this.parentMenu = parentMenu;
34304         Roo.menu.BaseItem.superclass.render.call(this, container);
34305         this.container.menuItemId = this.id;
34306     },
34307
34308     // private
34309     onRender : function(container, position){
34310         this.el = Roo.get(this.el);
34311         container.dom.appendChild(this.el.dom);
34312     },
34313
34314     // private
34315     onClick : function(e){
34316         if(!this.disabled && this.fireEvent("click", this, e) !== false
34317                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34318             this.handleClick(e);
34319         }else{
34320             e.stopEvent();
34321         }
34322     },
34323
34324     // private
34325     activate : function(){
34326         if(this.disabled){
34327             return false;
34328         }
34329         var li = this.container;
34330         li.addClass(this.activeClass);
34331         this.region = li.getRegion().adjust(2, 2, -2, -2);
34332         this.fireEvent("activate", this);
34333         return true;
34334     },
34335
34336     // private
34337     deactivate : function(){
34338         this.container.removeClass(this.activeClass);
34339         this.fireEvent("deactivate", this);
34340     },
34341
34342     // private
34343     shouldDeactivate : function(e){
34344         return !this.region || !this.region.contains(e.getPoint());
34345     },
34346
34347     // private
34348     handleClick : function(e){
34349         if(this.hideOnClick){
34350             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34351         }
34352     },
34353
34354     // private
34355     expandMenu : function(autoActivate){
34356         // do nothing
34357     },
34358
34359     // private
34360     hideMenu : function(){
34361         // do nothing
34362     }
34363 });/*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373  
34374 /**
34375  * @class Roo.menu.Adapter
34376  * @extends Roo.menu.BaseItem
34377  * 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.
34378  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34379  * @constructor
34380  * Creates a new Adapter
34381  * @param {Object} config Configuration options
34382  */
34383 Roo.menu.Adapter = function(component, config){
34384     Roo.menu.Adapter.superclass.constructor.call(this, config);
34385     this.component = component;
34386 };
34387 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34388     // private
34389     canActivate : true,
34390
34391     // private
34392     onRender : function(container, position){
34393         this.component.render(container);
34394         this.el = this.component.getEl();
34395     },
34396
34397     // private
34398     activate : function(){
34399         if(this.disabled){
34400             return false;
34401         }
34402         this.component.focus();
34403         this.fireEvent("activate", this);
34404         return true;
34405     },
34406
34407     // private
34408     deactivate : function(){
34409         this.fireEvent("deactivate", this);
34410     },
34411
34412     // private
34413     disable : function(){
34414         this.component.disable();
34415         Roo.menu.Adapter.superclass.disable.call(this);
34416     },
34417
34418     // private
34419     enable : function(){
34420         this.component.enable();
34421         Roo.menu.Adapter.superclass.enable.call(this);
34422     }
34423 });/*
34424  * Based on:
34425  * Ext JS Library 1.1.1
34426  * Copyright(c) 2006-2007, Ext JS, LLC.
34427  *
34428  * Originally Released Under LGPL - original licence link has changed is not relivant.
34429  *
34430  * Fork - LGPL
34431  * <script type="text/javascript">
34432  */
34433
34434 /**
34435  * @class Roo.menu.TextItem
34436  * @extends Roo.menu.BaseItem
34437  * Adds a static text string to a menu, usually used as either a heading or group separator.
34438  * Note: old style constructor with text is still supported.
34439  * 
34440  * @constructor
34441  * Creates a new TextItem
34442  * @param {Object} cfg Configuration
34443  */
34444 Roo.menu.TextItem = function(cfg){
34445     if (typeof(cfg) == 'string') {
34446         this.text = cfg;
34447     } else {
34448         Roo.apply(this,cfg);
34449     }
34450     
34451     Roo.menu.TextItem.superclass.constructor.call(this);
34452 };
34453
34454 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34455     /**
34456      * @cfg {Boolean} text Text to show on item.
34457      */
34458     text : '',
34459     
34460     /**
34461      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34462      */
34463     hideOnClick : false,
34464     /**
34465      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34466      */
34467     itemCls : "x-menu-text",
34468
34469     // private
34470     onRender : function(){
34471         var s = document.createElement("span");
34472         s.className = this.itemCls;
34473         s.innerHTML = this.text;
34474         this.el = s;
34475         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34476     }
34477 });/*
34478  * Based on:
34479  * Ext JS Library 1.1.1
34480  * Copyright(c) 2006-2007, Ext JS, LLC.
34481  *
34482  * Originally Released Under LGPL - original licence link has changed is not relivant.
34483  *
34484  * Fork - LGPL
34485  * <script type="text/javascript">
34486  */
34487
34488 /**
34489  * @class Roo.menu.Separator
34490  * @extends Roo.menu.BaseItem
34491  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34492  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34493  * @constructor
34494  * @param {Object} config Configuration options
34495  */
34496 Roo.menu.Separator = function(config){
34497     Roo.menu.Separator.superclass.constructor.call(this, config);
34498 };
34499
34500 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34501     /**
34502      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34503      */
34504     itemCls : "x-menu-sep",
34505     /**
34506      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34507      */
34508     hideOnClick : false,
34509
34510     // private
34511     onRender : function(li){
34512         var s = document.createElement("span");
34513         s.className = this.itemCls;
34514         s.innerHTML = "&#160;";
34515         this.el = s;
34516         li.addClass("x-menu-sep-li");
34517         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34518     }
34519 });/*
34520  * Based on:
34521  * Ext JS Library 1.1.1
34522  * Copyright(c) 2006-2007, Ext JS, LLC.
34523  *
34524  * Originally Released Under LGPL - original licence link has changed is not relivant.
34525  *
34526  * Fork - LGPL
34527  * <script type="text/javascript">
34528  */
34529 /**
34530  * @class Roo.menu.Item
34531  * @extends Roo.menu.BaseItem
34532  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34533  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34534  * activation and click handling.
34535  * @constructor
34536  * Creates a new Item
34537  * @param {Object} config Configuration options
34538  */
34539 Roo.menu.Item = function(config){
34540     Roo.menu.Item.superclass.constructor.call(this, config);
34541     if(this.menu){
34542         this.menu = Roo.menu.MenuMgr.get(this.menu);
34543     }
34544 };
34545 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34546     
34547     /**
34548      * @cfg {String} text
34549      * The text to show on the menu item.
34550      */
34551     text: '',
34552     /**
34553      * @cfg {String} icon
34554      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34555      */
34556     icon: undefined,
34557     /**
34558      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34559      */
34560     itemCls : "x-menu-item",
34561     /**
34562      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34563      */
34564     canActivate : true,
34565     /**
34566      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34567      */
34568     showDelay: 200,
34569     // doc'd in BaseItem
34570     hideDelay: 200,
34571
34572     // private
34573     ctype: "Roo.menu.Item",
34574     
34575     // private
34576     onRender : function(container, position){
34577         var el = document.createElement("a");
34578         el.hideFocus = true;
34579         el.unselectable = "on";
34580         el.href = this.href || "#";
34581         if(this.hrefTarget){
34582             el.target = this.hrefTarget;
34583         }
34584         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34585         el.innerHTML = String.format(
34586                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34587                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34588         this.el = el;
34589         Roo.menu.Item.superclass.onRender.call(this, container, position);
34590     },
34591
34592     /**
34593      * Sets the text to display in this menu item
34594      * @param {String} text The text to display
34595      */
34596     setText : function(text){
34597         this.text = text;
34598         if(this.rendered){
34599             this.el.update(String.format(
34600                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34601                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34602             this.parentMenu.autoWidth();
34603         }
34604     },
34605
34606     // private
34607     handleClick : function(e){
34608         if(!this.href){ // if no link defined, stop the event automatically
34609             e.stopEvent();
34610         }
34611         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34612     },
34613
34614     // private
34615     activate : function(autoExpand){
34616         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34617             this.focus();
34618             if(autoExpand){
34619                 this.expandMenu();
34620             }
34621         }
34622         return true;
34623     },
34624
34625     // private
34626     shouldDeactivate : function(e){
34627         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34628             if(this.menu && this.menu.isVisible()){
34629                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34630             }
34631             return true;
34632         }
34633         return false;
34634     },
34635
34636     // private
34637     deactivate : function(){
34638         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34639         this.hideMenu();
34640     },
34641
34642     // private
34643     expandMenu : function(autoActivate){
34644         if(!this.disabled && this.menu){
34645             clearTimeout(this.hideTimer);
34646             delete this.hideTimer;
34647             if(!this.menu.isVisible() && !this.showTimer){
34648                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34649             }else if (this.menu.isVisible() && autoActivate){
34650                 this.menu.tryActivate(0, 1);
34651             }
34652         }
34653     },
34654
34655     // private
34656     deferExpand : function(autoActivate){
34657         delete this.showTimer;
34658         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34659         if(autoActivate){
34660             this.menu.tryActivate(0, 1);
34661         }
34662     },
34663
34664     // private
34665     hideMenu : function(){
34666         clearTimeout(this.showTimer);
34667         delete this.showTimer;
34668         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34669             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34670         }
34671     },
34672
34673     // private
34674     deferHide : function(){
34675         delete this.hideTimer;
34676         this.menu.hide();
34677     }
34678 });/*
34679  * Based on:
34680  * Ext JS Library 1.1.1
34681  * Copyright(c) 2006-2007, Ext JS, LLC.
34682  *
34683  * Originally Released Under LGPL - original licence link has changed is not relivant.
34684  *
34685  * Fork - LGPL
34686  * <script type="text/javascript">
34687  */
34688  
34689 /**
34690  * @class Roo.menu.CheckItem
34691  * @extends Roo.menu.Item
34692  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34693  * @constructor
34694  * Creates a new CheckItem
34695  * @param {Object} config Configuration options
34696  */
34697 Roo.menu.CheckItem = function(config){
34698     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34699     this.addEvents({
34700         /**
34701          * @event beforecheckchange
34702          * Fires before the checked value is set, providing an opportunity to cancel if needed
34703          * @param {Roo.menu.CheckItem} this
34704          * @param {Boolean} checked The new checked value that will be set
34705          */
34706         "beforecheckchange" : true,
34707         /**
34708          * @event checkchange
34709          * Fires after the checked value has been set
34710          * @param {Roo.menu.CheckItem} this
34711          * @param {Boolean} checked The checked value that was set
34712          */
34713         "checkchange" : true
34714     });
34715     if(this.checkHandler){
34716         this.on('checkchange', this.checkHandler, this.scope);
34717     }
34718 };
34719 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34720     /**
34721      * @cfg {String} group
34722      * All check items with the same group name will automatically be grouped into a single-select
34723      * radio button group (defaults to '')
34724      */
34725     /**
34726      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34727      */
34728     itemCls : "x-menu-item x-menu-check-item",
34729     /**
34730      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34731      */
34732     groupClass : "x-menu-group-item",
34733
34734     /**
34735      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34736      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34737      * initialized with checked = true will be rendered as checked.
34738      */
34739     checked: false,
34740
34741     // private
34742     ctype: "Roo.menu.CheckItem",
34743
34744     // private
34745     onRender : function(c){
34746         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34747         if(this.group){
34748             this.el.addClass(this.groupClass);
34749         }
34750         Roo.menu.MenuMgr.registerCheckable(this);
34751         if(this.checked){
34752             this.checked = false;
34753             this.setChecked(true, true);
34754         }
34755     },
34756
34757     // private
34758     destroy : function(){
34759         if(this.rendered){
34760             Roo.menu.MenuMgr.unregisterCheckable(this);
34761         }
34762         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34763     },
34764
34765     /**
34766      * Set the checked state of this item
34767      * @param {Boolean} checked The new checked value
34768      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34769      */
34770     setChecked : function(state, suppressEvent){
34771         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34772             if(this.container){
34773                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34774             }
34775             this.checked = state;
34776             if(suppressEvent !== true){
34777                 this.fireEvent("checkchange", this, state);
34778             }
34779         }
34780     },
34781
34782     // private
34783     handleClick : function(e){
34784        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34785            this.setChecked(!this.checked);
34786        }
34787        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34788     }
34789 });/*
34790  * Based on:
34791  * Ext JS Library 1.1.1
34792  * Copyright(c) 2006-2007, Ext JS, LLC.
34793  *
34794  * Originally Released Under LGPL - original licence link has changed is not relivant.
34795  *
34796  * Fork - LGPL
34797  * <script type="text/javascript">
34798  */
34799  
34800 /**
34801  * @class Roo.menu.DateItem
34802  * @extends Roo.menu.Adapter
34803  * A menu item that wraps the {@link Roo.DatPicker} component.
34804  * @constructor
34805  * Creates a new DateItem
34806  * @param {Object} config Configuration options
34807  */
34808 Roo.menu.DateItem = function(config){
34809     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34810     /** The Roo.DatePicker object @type Roo.DatePicker */
34811     this.picker = this.component;
34812     this.addEvents({select: true});
34813     
34814     this.picker.on("render", function(picker){
34815         picker.getEl().swallowEvent("click");
34816         picker.container.addClass("x-menu-date-item");
34817     });
34818
34819     this.picker.on("select", this.onSelect, this);
34820 };
34821
34822 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34823     // private
34824     onSelect : function(picker, date){
34825         this.fireEvent("select", this, date, picker);
34826         Roo.menu.DateItem.superclass.handleClick.call(this);
34827     }
34828 });/*
34829  * Based on:
34830  * Ext JS Library 1.1.1
34831  * Copyright(c) 2006-2007, Ext JS, LLC.
34832  *
34833  * Originally Released Under LGPL - original licence link has changed is not relivant.
34834  *
34835  * Fork - LGPL
34836  * <script type="text/javascript">
34837  */
34838  
34839 /**
34840  * @class Roo.menu.ColorItem
34841  * @extends Roo.menu.Adapter
34842  * A menu item that wraps the {@link Roo.ColorPalette} component.
34843  * @constructor
34844  * Creates a new ColorItem
34845  * @param {Object} config Configuration options
34846  */
34847 Roo.menu.ColorItem = function(config){
34848     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34849     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34850     this.palette = this.component;
34851     this.relayEvents(this.palette, ["select"]);
34852     if(this.selectHandler){
34853         this.on('select', this.selectHandler, this.scope);
34854     }
34855 };
34856 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34857  * Based on:
34858  * Ext JS Library 1.1.1
34859  * Copyright(c) 2006-2007, Ext JS, LLC.
34860  *
34861  * Originally Released Under LGPL - original licence link has changed is not relivant.
34862  *
34863  * Fork - LGPL
34864  * <script type="text/javascript">
34865  */
34866  
34867
34868 /**
34869  * @class Roo.menu.DateMenu
34870  * @extends Roo.menu.Menu
34871  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34872  * @constructor
34873  * Creates a new DateMenu
34874  * @param {Object} config Configuration options
34875  */
34876 Roo.menu.DateMenu = function(config){
34877     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34878     this.plain = true;
34879     var di = new Roo.menu.DateItem(config);
34880     this.add(di);
34881     /**
34882      * The {@link Roo.DatePicker} instance for this DateMenu
34883      * @type DatePicker
34884      */
34885     this.picker = di.picker;
34886     /**
34887      * @event select
34888      * @param {DatePicker} picker
34889      * @param {Date} date
34890      */
34891     this.relayEvents(di, ["select"]);
34892
34893     this.on('beforeshow', function(){
34894         if(this.picker){
34895             this.picker.hideMonthPicker(true);
34896         }
34897     }, this);
34898 };
34899 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34900     cls:'x-date-menu'
34901 });/*
34902  * Based on:
34903  * Ext JS Library 1.1.1
34904  * Copyright(c) 2006-2007, Ext JS, LLC.
34905  *
34906  * Originally Released Under LGPL - original licence link has changed is not relivant.
34907  *
34908  * Fork - LGPL
34909  * <script type="text/javascript">
34910  */
34911  
34912
34913 /**
34914  * @class Roo.menu.ColorMenu
34915  * @extends Roo.menu.Menu
34916  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34917  * @constructor
34918  * Creates a new ColorMenu
34919  * @param {Object} config Configuration options
34920  */
34921 Roo.menu.ColorMenu = function(config){
34922     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34923     this.plain = true;
34924     var ci = new Roo.menu.ColorItem(config);
34925     this.add(ci);
34926     /**
34927      * The {@link Roo.ColorPalette} instance for this ColorMenu
34928      * @type ColorPalette
34929      */
34930     this.palette = ci.palette;
34931     /**
34932      * @event select
34933      * @param {ColorPalette} palette
34934      * @param {String} color
34935      */
34936     this.relayEvents(ci, ["select"]);
34937 };
34938 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34939  * Based on:
34940  * Ext JS Library 1.1.1
34941  * Copyright(c) 2006-2007, Ext JS, LLC.
34942  *
34943  * Originally Released Under LGPL - original licence link has changed is not relivant.
34944  *
34945  * Fork - LGPL
34946  * <script type="text/javascript">
34947  */
34948  
34949 /**
34950  * @class Roo.form.Field
34951  * @extends Roo.BoxComponent
34952  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34953  * @constructor
34954  * Creates a new Field
34955  * @param {Object} config Configuration options
34956  */
34957 Roo.form.Field = function(config){
34958     Roo.form.Field.superclass.constructor.call(this, config);
34959 };
34960
34961 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34962     /**
34963      * @cfg {String} fieldLabel Label to use when rendering a form.
34964      */
34965        /**
34966      * @cfg {String} qtip Mouse over tip
34967      */
34968      
34969     /**
34970      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34971      */
34972     invalidClass : "x-form-invalid",
34973     /**
34974      * @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")
34975      */
34976     invalidText : "The value in this field is invalid",
34977     /**
34978      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34979      */
34980     focusClass : "x-form-focus",
34981     /**
34982      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34983       automatic validation (defaults to "keyup").
34984      */
34985     validationEvent : "keyup",
34986     /**
34987      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34988      */
34989     validateOnBlur : true,
34990     /**
34991      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34992      */
34993     validationDelay : 250,
34994     /**
34995      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34996      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34997      */
34998     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34999     /**
35000      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35001      */
35002     fieldClass : "x-form-field",
35003     /**
35004      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35005      *<pre>
35006 Value         Description
35007 -----------   ----------------------------------------------------------------------
35008 qtip          Display a quick tip when the user hovers over the field
35009 title         Display a default browser title attribute popup
35010 under         Add a block div beneath the field containing the error text
35011 side          Add an error icon to the right of the field with a popup on hover
35012 [element id]  Add the error text directly to the innerHTML of the specified element
35013 </pre>
35014      */
35015     msgTarget : 'qtip',
35016     /**
35017      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35018      */
35019     msgFx : 'normal',
35020
35021     /**
35022      * @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.
35023      */
35024     readOnly : false,
35025
35026     /**
35027      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35028      */
35029     disabled : false,
35030
35031     /**
35032      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35033      */
35034     inputType : undefined,
35035     
35036     /**
35037      * @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).
35038          */
35039         tabIndex : undefined,
35040         
35041     // private
35042     isFormField : true,
35043
35044     // private
35045     hasFocus : false,
35046     /**
35047      * @property {Roo.Element} fieldEl
35048      * Element Containing the rendered Field (with label etc.)
35049      */
35050     /**
35051      * @cfg {Mixed} value A value to initialize this field with.
35052      */
35053     value : undefined,
35054
35055     /**
35056      * @cfg {String} name The field's HTML name attribute.
35057      */
35058     /**
35059      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35060      */
35061
35062         // private ??
35063         initComponent : function(){
35064         Roo.form.Field.superclass.initComponent.call(this);
35065         this.addEvents({
35066             /**
35067              * @event focus
35068              * Fires when this field receives input focus.
35069              * @param {Roo.form.Field} this
35070              */
35071             focus : true,
35072             /**
35073              * @event blur
35074              * Fires when this field loses input focus.
35075              * @param {Roo.form.Field} this
35076              */
35077             blur : true,
35078             /**
35079              * @event specialkey
35080              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35081              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35082              * @param {Roo.form.Field} this
35083              * @param {Roo.EventObject} e The event object
35084              */
35085             specialkey : true,
35086             /**
35087              * @event change
35088              * Fires just before the field blurs if the field value has changed.
35089              * @param {Roo.form.Field} this
35090              * @param {Mixed} newValue The new value
35091              * @param {Mixed} oldValue The original value
35092              */
35093             change : true,
35094             /**
35095              * @event invalid
35096              * Fires after the field has been marked as invalid.
35097              * @param {Roo.form.Field} this
35098              * @param {String} msg The validation message
35099              */
35100             invalid : true,
35101             /**
35102              * @event valid
35103              * Fires after the field has been validated with no errors.
35104              * @param {Roo.form.Field} this
35105              */
35106             valid : true
35107         });
35108     },
35109
35110     /**
35111      * Returns the name attribute of the field if available
35112      * @return {String} name The field name
35113      */
35114     getName: function(){
35115          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35116     },
35117
35118     // private
35119     onRender : function(ct, position){
35120         Roo.form.Field.superclass.onRender.call(this, ct, position);
35121         if(!this.el){
35122             var cfg = this.getAutoCreate();
35123             if(!cfg.name){
35124                 cfg.name = this.name || this.id;
35125             }
35126             if(this.inputType){
35127                 cfg.type = this.inputType;
35128             }
35129             this.el = ct.createChild(cfg, position);
35130         }
35131         var type = this.el.dom.type;
35132         if(type){
35133             if(type == 'password'){
35134                 type = 'text';
35135             }
35136             this.el.addClass('x-form-'+type);
35137         }
35138         if(this.readOnly){
35139             this.el.dom.readOnly = true;
35140         }
35141         if(this.tabIndex !== undefined){
35142             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35143         }
35144
35145         this.el.addClass([this.fieldClass, this.cls]);
35146         this.initValue();
35147     },
35148
35149     /**
35150      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35151      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35152      * @return {Roo.form.Field} this
35153      */
35154     applyTo : function(target){
35155         this.allowDomMove = false;
35156         this.el = Roo.get(target);
35157         this.render(this.el.dom.parentNode);
35158         return this;
35159     },
35160
35161     // private
35162     initValue : function(){
35163         if(this.value !== undefined){
35164             this.setValue(this.value);
35165         }else if(this.el.dom.value.length > 0){
35166             this.setValue(this.el.dom.value);
35167         }
35168     },
35169
35170     /**
35171      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35172      */
35173     isDirty : function() {
35174         if(this.disabled) {
35175             return false;
35176         }
35177         return String(this.getValue()) !== String(this.originalValue);
35178     },
35179
35180     // private
35181     afterRender : function(){
35182         Roo.form.Field.superclass.afterRender.call(this);
35183         this.initEvents();
35184     },
35185
35186     // private
35187     fireKey : function(e){
35188         if(e.isNavKeyPress()){
35189             this.fireEvent("specialkey", this, e);
35190         }
35191     },
35192
35193     /**
35194      * Resets the current field value to the originally loaded value and clears any validation messages
35195      */
35196     reset : function(){
35197         this.setValue(this.originalValue);
35198         this.clearInvalid();
35199     },
35200
35201     // private
35202     initEvents : function(){
35203         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35204         this.el.on("focus", this.onFocus,  this);
35205         this.el.on("blur", this.onBlur,  this);
35206
35207         // reference to original value for reset
35208         this.originalValue = this.getValue();
35209     },
35210
35211     // private
35212     onFocus : function(){
35213         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35214             this.el.addClass(this.focusClass);
35215         }
35216         if(!this.hasFocus){
35217             this.hasFocus = true;
35218             this.startValue = this.getValue();
35219             this.fireEvent("focus", this);
35220         }
35221     },
35222
35223     beforeBlur : Roo.emptyFn,
35224
35225     // private
35226     onBlur : function(){
35227         this.beforeBlur();
35228         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35229             this.el.removeClass(this.focusClass);
35230         }
35231         this.hasFocus = false;
35232         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35233             this.validate();
35234         }
35235         var v = this.getValue();
35236         if(String(v) !== String(this.startValue)){
35237             this.fireEvent('change', this, v, this.startValue);
35238         }
35239         this.fireEvent("blur", this);
35240     },
35241
35242     /**
35243      * Returns whether or not the field value is currently valid
35244      * @param {Boolean} preventMark True to disable marking the field invalid
35245      * @return {Boolean} True if the value is valid, else false
35246      */
35247     isValid : function(preventMark){
35248         if(this.disabled){
35249             return true;
35250         }
35251         var restore = this.preventMark;
35252         this.preventMark = preventMark === true;
35253         var v = this.validateValue(this.processValue(this.getRawValue()));
35254         this.preventMark = restore;
35255         return v;
35256     },
35257
35258     /**
35259      * Validates the field value
35260      * @return {Boolean} True if the value is valid, else false
35261      */
35262     validate : function(){
35263         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35264             this.clearInvalid();
35265             return true;
35266         }
35267         return false;
35268     },
35269
35270     processValue : function(value){
35271         return value;
35272     },
35273
35274     // private
35275     // Subclasses should provide the validation implementation by overriding this
35276     validateValue : function(value){
35277         return true;
35278     },
35279
35280     /**
35281      * Mark this field as invalid
35282      * @param {String} msg The validation message
35283      */
35284     markInvalid : function(msg){
35285         if(!this.rendered || this.preventMark){ // not rendered
35286             return;
35287         }
35288         this.el.addClass(this.invalidClass);
35289         msg = msg || this.invalidText;
35290         switch(this.msgTarget){
35291             case 'qtip':
35292                 this.el.dom.qtip = msg;
35293                 this.el.dom.qclass = 'x-form-invalid-tip';
35294                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35295                     Roo.QuickTips.enable();
35296                 }
35297                 break;
35298             case 'title':
35299                 this.el.dom.title = msg;
35300                 break;
35301             case 'under':
35302                 if(!this.errorEl){
35303                     var elp = this.el.findParent('.x-form-element', 5, true);
35304                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35305                     this.errorEl.setWidth(elp.getWidth(true)-20);
35306                 }
35307                 this.errorEl.update(msg);
35308                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35309                 break;
35310             case 'side':
35311                 if(!this.errorIcon){
35312                     var elp = this.el.findParent('.x-form-element', 5, true);
35313                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35314                 }
35315                 this.alignErrorIcon();
35316                 this.errorIcon.dom.qtip = msg;
35317                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35318                 this.errorIcon.show();
35319                 this.on('resize', this.alignErrorIcon, this);
35320                 break;
35321             default:
35322                 var t = Roo.getDom(this.msgTarget);
35323                 t.innerHTML = msg;
35324                 t.style.display = this.msgDisplay;
35325                 break;
35326         }
35327         this.fireEvent('invalid', this, msg);
35328     },
35329
35330     // private
35331     alignErrorIcon : function(){
35332         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35333     },
35334
35335     /**
35336      * Clear any invalid styles/messages for this field
35337      */
35338     clearInvalid : function(){
35339         if(!this.rendered || this.preventMark){ // not rendered
35340             return;
35341         }
35342         this.el.removeClass(this.invalidClass);
35343         switch(this.msgTarget){
35344             case 'qtip':
35345                 this.el.dom.qtip = '';
35346                 break;
35347             case 'title':
35348                 this.el.dom.title = '';
35349                 break;
35350             case 'under':
35351                 if(this.errorEl){
35352                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35353                 }
35354                 break;
35355             case 'side':
35356                 if(this.errorIcon){
35357                     this.errorIcon.dom.qtip = '';
35358                     this.errorIcon.hide();
35359                     this.un('resize', this.alignErrorIcon, this);
35360                 }
35361                 break;
35362             default:
35363                 var t = Roo.getDom(this.msgTarget);
35364                 t.innerHTML = '';
35365                 t.style.display = 'none';
35366                 break;
35367         }
35368         this.fireEvent('valid', this);
35369     },
35370
35371     /**
35372      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35373      * @return {Mixed} value The field value
35374      */
35375     getRawValue : function(){
35376         var v = this.el.getValue();
35377         if(v === this.emptyText){
35378             v = '';
35379         }
35380         return v;
35381     },
35382
35383     /**
35384      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35385      * @return {Mixed} value The field value
35386      */
35387     getValue : function(){
35388         var v = this.el.getValue();
35389         if(v === this.emptyText || v === undefined){
35390             v = '';
35391         }
35392         return v;
35393     },
35394
35395     /**
35396      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35397      * @param {Mixed} value The value to set
35398      */
35399     setRawValue : function(v){
35400         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35401     },
35402
35403     /**
35404      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35405      * @param {Mixed} value The value to set
35406      */
35407     setValue : function(v){
35408         this.value = v;
35409         if(this.rendered){
35410             this.el.dom.value = (v === null || v === undefined ? '' : v);
35411             this.validate();
35412         }
35413     },
35414
35415     adjustSize : function(w, h){
35416         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35417         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35418         return s;
35419     },
35420
35421     adjustWidth : function(tag, w){
35422         tag = tag.toLowerCase();
35423         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35424             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35425                 if(tag == 'input'){
35426                     return w + 2;
35427                 }
35428                 if(tag = 'textarea'){
35429                     return w-2;
35430                 }
35431             }else if(Roo.isOpera){
35432                 if(tag == 'input'){
35433                     return w + 2;
35434                 }
35435                 if(tag = 'textarea'){
35436                     return w-2;
35437                 }
35438             }
35439         }
35440         return w;
35441     }
35442 });
35443
35444
35445 // anything other than normal should be considered experimental
35446 Roo.form.Field.msgFx = {
35447     normal : {
35448         show: function(msgEl, f){
35449             msgEl.setDisplayed('block');
35450         },
35451
35452         hide : function(msgEl, f){
35453             msgEl.setDisplayed(false).update('');
35454         }
35455     },
35456
35457     slide : {
35458         show: function(msgEl, f){
35459             msgEl.slideIn('t', {stopFx:true});
35460         },
35461
35462         hide : function(msgEl, f){
35463             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35464         }
35465     },
35466
35467     slideRight : {
35468         show: function(msgEl, f){
35469             msgEl.fixDisplay();
35470             msgEl.alignTo(f.el, 'tl-tr');
35471             msgEl.slideIn('l', {stopFx:true});
35472         },
35473
35474         hide : function(msgEl, f){
35475             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35476         }
35477     }
35478 };/*
35479  * Based on:
35480  * Ext JS Library 1.1.1
35481  * Copyright(c) 2006-2007, Ext JS, LLC.
35482  *
35483  * Originally Released Under LGPL - original licence link has changed is not relivant.
35484  *
35485  * Fork - LGPL
35486  * <script type="text/javascript">
35487  */
35488  
35489
35490 /**
35491  * @class Roo.form.TextField
35492  * @extends Roo.form.Field
35493  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35494  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35495  * @constructor
35496  * Creates a new TextField
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.form.TextField = function(config){
35500     Roo.form.TextField.superclass.constructor.call(this, config);
35501     this.addEvents({
35502         /**
35503          * @event autosize
35504          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35505          * according to the default logic, but this event provides a hook for the developer to apply additional
35506          * logic at runtime to resize the field if needed.
35507              * @param {Roo.form.Field} this This text field
35508              * @param {Number} width The new field width
35509              */
35510         autosize : true
35511     });
35512 };
35513
35514 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35515     /**
35516      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35517      */
35518     grow : false,
35519     /**
35520      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35521      */
35522     growMin : 30,
35523     /**
35524      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35525      */
35526     growMax : 800,
35527     /**
35528      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35529      */
35530     vtype : null,
35531     /**
35532      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35533      */
35534     maskRe : null,
35535     /**
35536      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35537      */
35538     disableKeyFilter : false,
35539     /**
35540      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35541      */
35542     allowBlank : true,
35543     /**
35544      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35545      */
35546     minLength : 0,
35547     /**
35548      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35549      */
35550     maxLength : Number.MAX_VALUE,
35551     /**
35552      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35553      */
35554     minLengthText : "The minimum length for this field is {0}",
35555     /**
35556      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35557      */
35558     maxLengthText : "The maximum length for this field is {0}",
35559     /**
35560      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35561      */
35562     selectOnFocus : false,
35563     /**
35564      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35565      */
35566     blankText : "This field is required",
35567     /**
35568      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35569      * If available, this function will be called only after the basic validators all return true, and will be passed the
35570      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35571      */
35572     validator : null,
35573     /**
35574      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35575      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35576      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35577      */
35578     regex : null,
35579     /**
35580      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35581      */
35582     regexText : "",
35583     /**
35584      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35585      */
35586     emptyText : null,
35587     /**
35588      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35589      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35590      */
35591     emptyClass : 'x-form-empty-field',
35592
35593     // private
35594     initEvents : function(){
35595         Roo.form.TextField.superclass.initEvents.call(this);
35596         if(this.validationEvent == 'keyup'){
35597             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35598             this.el.on('keyup', this.filterValidation, this);
35599         }
35600         else if(this.validationEvent !== false){
35601             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35602         }
35603         if(this.selectOnFocus || this.emptyText){
35604             this.on("focus", this.preFocus, this);
35605             if(this.emptyText){
35606                 this.on('blur', this.postBlur, this);
35607                 this.applyEmptyText();
35608             }
35609         }
35610         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35611             this.el.on("keypress", this.filterKeys, this);
35612         }
35613         if(this.grow){
35614             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35615             this.el.on("click", this.autoSize,  this);
35616         }
35617     },
35618
35619     processValue : function(value){
35620         if(this.stripCharsRe){
35621             var newValue = value.replace(this.stripCharsRe, '');
35622             if(newValue !== value){
35623                 this.setRawValue(newValue);
35624                 return newValue;
35625             }
35626         }
35627         return value;
35628     },
35629
35630     filterValidation : function(e){
35631         if(!e.isNavKeyPress()){
35632             this.validationTask.delay(this.validationDelay);
35633         }
35634     },
35635
35636     // private
35637     onKeyUp : function(e){
35638         if(!e.isNavKeyPress()){
35639             this.autoSize();
35640         }
35641     },
35642
35643     /**
35644      * Resets the current field value to the originally-loaded value and clears any validation messages.
35645      * Also adds emptyText and emptyClass if the original value was blank.
35646      */
35647     reset : function(){
35648         Roo.form.TextField.superclass.reset.call(this);
35649         this.applyEmptyText();
35650     },
35651
35652     applyEmptyText : function(){
35653         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35654             this.setRawValue(this.emptyText);
35655             this.el.addClass(this.emptyClass);
35656         }
35657     },
35658
35659     // private
35660     preFocus : function(){
35661         if(this.emptyText){
35662             if(this.el.dom.value == this.emptyText){
35663                 this.setRawValue('');
35664             }
35665             this.el.removeClass(this.emptyClass);
35666         }
35667         if(this.selectOnFocus){
35668             this.el.dom.select();
35669         }
35670     },
35671
35672     // private
35673     postBlur : function(){
35674         this.applyEmptyText();
35675     },
35676
35677     // private
35678     filterKeys : function(e){
35679         var k = e.getKey();
35680         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35681             return;
35682         }
35683         var c = e.getCharCode(), cc = String.fromCharCode(c);
35684         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35685             return;
35686         }
35687         if(!this.maskRe.test(cc)){
35688             e.stopEvent();
35689         }
35690     },
35691
35692     setValue : function(v){
35693         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35694             this.el.removeClass(this.emptyClass);
35695         }
35696         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35697         this.applyEmptyText();
35698         this.autoSize();
35699     },
35700
35701     /**
35702      * Validates a value according to the field's validation rules and marks the field as invalid
35703      * if the validation fails
35704      * @param {Mixed} value The value to validate
35705      * @return {Boolean} True if the value is valid, else false
35706      */
35707     validateValue : function(value){
35708         if(value.length < 1 || value === this.emptyText){ // if it's blank
35709              if(this.allowBlank){
35710                 this.clearInvalid();
35711                 return true;
35712              }else{
35713                 this.markInvalid(this.blankText);
35714                 return false;
35715              }
35716         }
35717         if(value.length < this.minLength){
35718             this.markInvalid(String.format(this.minLengthText, this.minLength));
35719             return false;
35720         }
35721         if(value.length > this.maxLength){
35722             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35723             return false;
35724         }
35725         if(this.vtype){
35726             var vt = Roo.form.VTypes;
35727             if(!vt[this.vtype](value, this)){
35728                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35729                 return false;
35730             }
35731         }
35732         if(typeof this.validator == "function"){
35733             var msg = this.validator(value);
35734             if(msg !== true){
35735                 this.markInvalid(msg);
35736                 return false;
35737             }
35738         }
35739         if(this.regex && !this.regex.test(value)){
35740             this.markInvalid(this.regexText);
35741             return false;
35742         }
35743         return true;
35744     },
35745
35746     /**
35747      * Selects text in this field
35748      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35749      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35750      */
35751     selectText : function(start, end){
35752         var v = this.getRawValue();
35753         if(v.length > 0){
35754             start = start === undefined ? 0 : start;
35755             end = end === undefined ? v.length : end;
35756             var d = this.el.dom;
35757             if(d.setSelectionRange){
35758                 d.setSelectionRange(start, end);
35759             }else if(d.createTextRange){
35760                 var range = d.createTextRange();
35761                 range.moveStart("character", start);
35762                 range.moveEnd("character", v.length-end);
35763                 range.select();
35764             }
35765         }
35766     },
35767
35768     /**
35769      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35770      * This only takes effect if grow = true, and fires the autosize event.
35771      */
35772     autoSize : function(){
35773         if(!this.grow || !this.rendered){
35774             return;
35775         }
35776         if(!this.metrics){
35777             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35778         }
35779         var el = this.el;
35780         var v = el.dom.value;
35781         var d = document.createElement('div');
35782         d.appendChild(document.createTextNode(v));
35783         v = d.innerHTML;
35784         d = null;
35785         v += "&#160;";
35786         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35787         this.el.setWidth(w);
35788         this.fireEvent("autosize", this, w);
35789     }
35790 });/*
35791  * Based on:
35792  * Ext JS Library 1.1.1
35793  * Copyright(c) 2006-2007, Ext JS, LLC.
35794  *
35795  * Originally Released Under LGPL - original licence link has changed is not relivant.
35796  *
35797  * Fork - LGPL
35798  * <script type="text/javascript">
35799  */
35800  
35801 /**
35802  * @class Roo.form.Hidden
35803  * @extends Roo.form.TextField
35804  * Simple Hidden element used on forms 
35805  * 
35806  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35807  * 
35808  * @constructor
35809  * Creates a new Hidden form element.
35810  * @param {Object} config Configuration options
35811  */
35812
35813
35814
35815 // easy hidden field...
35816 Roo.form.Hidden = function(config){
35817     Roo.form.Hidden.superclass.constructor.call(this, config);
35818 };
35819   
35820 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35821     fieldLabel:      '',
35822     inputType:      'hidden',
35823     width:          50,
35824     allowBlank:     true,
35825     labelSeparator: '',
35826     hidden:         true,
35827     itemCls :       'x-form-item-display-none'
35828
35829
35830 });
35831
35832
35833 /*
35834  * Based on:
35835  * Ext JS Library 1.1.1
35836  * Copyright(c) 2006-2007, Ext JS, LLC.
35837  *
35838  * Originally Released Under LGPL - original licence link has changed is not relivant.
35839  *
35840  * Fork - LGPL
35841  * <script type="text/javascript">
35842  */
35843  
35844 /**
35845  * @class Roo.form.TriggerField
35846  * @extends Roo.form.TextField
35847  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35848  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35849  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35850  * for which you can provide a custom implementation.  For example:
35851  * <pre><code>
35852 var trigger = new Roo.form.TriggerField();
35853 trigger.onTriggerClick = myTriggerFn;
35854 trigger.applyTo('my-field');
35855 </code></pre>
35856  *
35857  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35858  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35859  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35860  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35861  * @constructor
35862  * Create a new TriggerField.
35863  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35864  * to the base TextField)
35865  */
35866 Roo.form.TriggerField = function(config){
35867     this.mimicing = false;
35868     Roo.form.TriggerField.superclass.constructor.call(this, config);
35869 };
35870
35871 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35872     /**
35873      * @cfg {String} triggerClass A CSS class to apply to the trigger
35874      */
35875     /**
35876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35877      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35878      */
35879     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35880     /**
35881      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35882      */
35883     hideTrigger:false,
35884
35885     /** @cfg {Boolean} grow @hide */
35886     /** @cfg {Number} growMin @hide */
35887     /** @cfg {Number} growMax @hide */
35888
35889     /**
35890      * @hide 
35891      * @method
35892      */
35893     autoSize: Roo.emptyFn,
35894     // private
35895     monitorTab : true,
35896     // private
35897     deferHeight : true,
35898
35899     
35900     actionMode : 'wrap',
35901     // private
35902     onResize : function(w, h){
35903         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35904         if(typeof w == 'number'){
35905             var x = w - this.trigger.getWidth();
35906             this.el.setWidth(this.adjustWidth('input', x));
35907             this.trigger.setStyle('left', x+'px');
35908         }
35909     },
35910
35911     // private
35912     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35913
35914     // private
35915     getResizeEl : function(){
35916         return this.wrap;
35917     },
35918
35919     // private
35920     getPositionEl : function(){
35921         return this.wrap;
35922     },
35923
35924     // private
35925     alignErrorIcon : function(){
35926         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35927     },
35928
35929     // private
35930     onRender : function(ct, position){
35931         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35932         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35933         this.trigger = this.wrap.createChild(this.triggerConfig ||
35934                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35935         if(this.hideTrigger){
35936             this.trigger.setDisplayed(false);
35937         }
35938         this.initTrigger();
35939         if(!this.width){
35940             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35941         }
35942     },
35943
35944     // private
35945     initTrigger : function(){
35946         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35947         this.trigger.addClassOnOver('x-form-trigger-over');
35948         this.trigger.addClassOnClick('x-form-trigger-click');
35949     },
35950
35951     // private
35952     onDestroy : function(){
35953         if(this.trigger){
35954             this.trigger.removeAllListeners();
35955             this.trigger.remove();
35956         }
35957         if(this.wrap){
35958             this.wrap.remove();
35959         }
35960         Roo.form.TriggerField.superclass.onDestroy.call(this);
35961     },
35962
35963     // private
35964     onFocus : function(){
35965         Roo.form.TriggerField.superclass.onFocus.call(this);
35966         if(!this.mimicing){
35967             this.wrap.addClass('x-trigger-wrap-focus');
35968             this.mimicing = true;
35969             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35970             if(this.monitorTab){
35971                 this.el.on("keydown", this.checkTab, this);
35972             }
35973         }
35974     },
35975
35976     // private
35977     checkTab : function(e){
35978         if(e.getKey() == e.TAB){
35979             this.triggerBlur();
35980         }
35981     },
35982
35983     // private
35984     onBlur : function(){
35985         // do nothing
35986     },
35987
35988     // private
35989     mimicBlur : function(e, t){
35990         if(!this.wrap.contains(t) && this.validateBlur()){
35991             this.triggerBlur();
35992         }
35993     },
35994
35995     // private
35996     triggerBlur : function(){
35997         this.mimicing = false;
35998         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35999         if(this.monitorTab){
36000             this.el.un("keydown", this.checkTab, this);
36001         }
36002         this.wrap.removeClass('x-trigger-wrap-focus');
36003         Roo.form.TriggerField.superclass.onBlur.call(this);
36004     },
36005
36006     // private
36007     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36008     validateBlur : function(e, t){
36009         return true;
36010     },
36011
36012     // private
36013     onDisable : function(){
36014         Roo.form.TriggerField.superclass.onDisable.call(this);
36015         if(this.wrap){
36016             this.wrap.addClass('x-item-disabled');
36017         }
36018     },
36019
36020     // private
36021     onEnable : function(){
36022         Roo.form.TriggerField.superclass.onEnable.call(this);
36023         if(this.wrap){
36024             this.wrap.removeClass('x-item-disabled');
36025         }
36026     },
36027
36028     // private
36029     onShow : function(){
36030         var ae = this.getActionEl();
36031         
36032         if(ae){
36033             ae.dom.style.display = '';
36034             ae.dom.style.visibility = 'visible';
36035         }
36036     },
36037
36038     // private
36039     
36040     onHide : function(){
36041         var ae = this.getActionEl();
36042         ae.dom.style.display = 'none';
36043     },
36044
36045     /**
36046      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36047      * by an implementing function.
36048      * @method
36049      * @param {EventObject} e
36050      */
36051     onTriggerClick : Roo.emptyFn
36052 });
36053
36054 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36055 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36056 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36057 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36058     initComponent : function(){
36059         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36060
36061         this.triggerConfig = {
36062             tag:'span', cls:'x-form-twin-triggers', cn:[
36063             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36064             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36065         ]};
36066     },
36067
36068     getTrigger : function(index){
36069         return this.triggers[index];
36070     },
36071
36072     initTrigger : function(){
36073         var ts = this.trigger.select('.x-form-trigger', true);
36074         this.wrap.setStyle('overflow', 'hidden');
36075         var triggerField = this;
36076         ts.each(function(t, all, index){
36077             t.hide = function(){
36078                 var w = triggerField.wrap.getWidth();
36079                 this.dom.style.display = 'none';
36080                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36081             };
36082             t.show = function(){
36083                 var w = triggerField.wrap.getWidth();
36084                 this.dom.style.display = '';
36085                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36086             };
36087             var triggerIndex = 'Trigger'+(index+1);
36088
36089             if(this['hide'+triggerIndex]){
36090                 t.dom.style.display = 'none';
36091             }
36092             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36093             t.addClassOnOver('x-form-trigger-over');
36094             t.addClassOnClick('x-form-trigger-click');
36095         }, this);
36096         this.triggers = ts.elements;
36097     },
36098
36099     onTrigger1Click : Roo.emptyFn,
36100     onTrigger2Click : Roo.emptyFn
36101 });/*
36102  * Based on:
36103  * Ext JS Library 1.1.1
36104  * Copyright(c) 2006-2007, Ext JS, LLC.
36105  *
36106  * Originally Released Under LGPL - original licence link has changed is not relivant.
36107  *
36108  * Fork - LGPL
36109  * <script type="text/javascript">
36110  */
36111  
36112 /**
36113  * @class Roo.form.TextArea
36114  * @extends Roo.form.TextField
36115  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36116  * support for auto-sizing.
36117  * @constructor
36118  * Creates a new TextArea
36119  * @param {Object} config Configuration options
36120  */
36121 Roo.form.TextArea = function(config){
36122     Roo.form.TextArea.superclass.constructor.call(this, config);
36123     // these are provided exchanges for backwards compat
36124     // minHeight/maxHeight were replaced by growMin/growMax to be
36125     // compatible with TextField growing config values
36126     if(this.minHeight !== undefined){
36127         this.growMin = this.minHeight;
36128     }
36129     if(this.maxHeight !== undefined){
36130         this.growMax = this.maxHeight;
36131     }
36132 };
36133
36134 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36135     /**
36136      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36137      */
36138     growMin : 60,
36139     /**
36140      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36141      */
36142     growMax: 1000,
36143     /**
36144      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36145      * in the field (equivalent to setting overflow: hidden, defaults to false)
36146      */
36147     preventScrollbars: false,
36148     /**
36149      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36150      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36151      */
36152
36153     // private
36154     onRender : function(ct, position){
36155         if(!this.el){
36156             this.defaultAutoCreate = {
36157                 tag: "textarea",
36158                 style:"width:300px;height:60px;",
36159                 autocomplete: "off"
36160             };
36161         }
36162         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36163         if(this.grow){
36164             this.textSizeEl = Roo.DomHelper.append(document.body, {
36165                 tag: "pre", cls: "x-form-grow-sizer"
36166             });
36167             if(this.preventScrollbars){
36168                 this.el.setStyle("overflow", "hidden");
36169             }
36170             this.el.setHeight(this.growMin);
36171         }
36172     },
36173
36174     onDestroy : function(){
36175         if(this.textSizeEl){
36176             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36177         }
36178         Roo.form.TextArea.superclass.onDestroy.call(this);
36179     },
36180
36181     // private
36182     onKeyUp : function(e){
36183         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36184             this.autoSize();
36185         }
36186     },
36187
36188     /**
36189      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36190      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36191      */
36192     autoSize : function(){
36193         if(!this.grow || !this.textSizeEl){
36194             return;
36195         }
36196         var el = this.el;
36197         var v = el.dom.value;
36198         var ts = this.textSizeEl;
36199
36200         ts.innerHTML = '';
36201         ts.appendChild(document.createTextNode(v));
36202         v = ts.innerHTML;
36203
36204         Roo.fly(ts).setWidth(this.el.getWidth());
36205         if(v.length < 1){
36206             v = "&#160;&#160;";
36207         }else{
36208             if(Roo.isIE){
36209                 v = v.replace(/\n/g, '<p>&#160;</p>');
36210             }
36211             v += "&#160;\n&#160;";
36212         }
36213         ts.innerHTML = v;
36214         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36215         if(h != this.lastHeight){
36216             this.lastHeight = h;
36217             this.el.setHeight(h);
36218             this.fireEvent("autosize", this, h);
36219         }
36220     }
36221 });/*
36222  * Based on:
36223  * Ext JS Library 1.1.1
36224  * Copyright(c) 2006-2007, Ext JS, LLC.
36225  *
36226  * Originally Released Under LGPL - original licence link has changed is not relivant.
36227  *
36228  * Fork - LGPL
36229  * <script type="text/javascript">
36230  */
36231  
36232
36233 /**
36234  * @class Roo.form.NumberField
36235  * @extends Roo.form.TextField
36236  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36237  * @constructor
36238  * Creates a new NumberField
36239  * @param {Object} config Configuration options
36240  */
36241 Roo.form.NumberField = function(config){
36242     Roo.form.NumberField.superclass.constructor.call(this, config);
36243 };
36244
36245 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36246     /**
36247      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36248      */
36249     fieldClass: "x-form-field x-form-num-field",
36250     /**
36251      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36252      */
36253     allowDecimals : true,
36254     /**
36255      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36256      */
36257     decimalSeparator : ".",
36258     /**
36259      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36260      */
36261     decimalPrecision : 2,
36262     /**
36263      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36264      */
36265     allowNegative : true,
36266     /**
36267      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36268      */
36269     minValue : Number.NEGATIVE_INFINITY,
36270     /**
36271      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36272      */
36273     maxValue : Number.MAX_VALUE,
36274     /**
36275      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36276      */
36277     minText : "The minimum value for this field is {0}",
36278     /**
36279      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36280      */
36281     maxText : "The maximum value for this field is {0}",
36282     /**
36283      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36284      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36285      */
36286     nanText : "{0} is not a valid number",
36287
36288     // private
36289     initEvents : function(){
36290         Roo.form.NumberField.superclass.initEvents.call(this);
36291         var allowed = "0123456789";
36292         if(this.allowDecimals){
36293             allowed += this.decimalSeparator;
36294         }
36295         if(this.allowNegative){
36296             allowed += "-";
36297         }
36298         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36299         var keyPress = function(e){
36300             var k = e.getKey();
36301             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36302                 return;
36303             }
36304             var c = e.getCharCode();
36305             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36306                 e.stopEvent();
36307             }
36308         };
36309         this.el.on("keypress", keyPress, this);
36310     },
36311
36312     // private
36313     validateValue : function(value){
36314         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36315             return false;
36316         }
36317         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36318              return true;
36319         }
36320         var num = this.parseValue(value);
36321         if(isNaN(num)){
36322             this.markInvalid(String.format(this.nanText, value));
36323             return false;
36324         }
36325         if(num < this.minValue){
36326             this.markInvalid(String.format(this.minText, this.minValue));
36327             return false;
36328         }
36329         if(num > this.maxValue){
36330             this.markInvalid(String.format(this.maxText, this.maxValue));
36331             return false;
36332         }
36333         return true;
36334     },
36335
36336     getValue : function(){
36337         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36338     },
36339
36340     // private
36341     parseValue : function(value){
36342         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36343         return isNaN(value) ? '' : value;
36344     },
36345
36346     // private
36347     fixPrecision : function(value){
36348         var nan = isNaN(value);
36349         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36350             return nan ? '' : value;
36351         }
36352         return parseFloat(value).toFixed(this.decimalPrecision);
36353     },
36354
36355     setValue : function(v){
36356         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36357     },
36358
36359     // private
36360     decimalPrecisionFcn : function(v){
36361         return Math.floor(v);
36362     },
36363
36364     beforeBlur : function(){
36365         var v = this.parseValue(this.getRawValue());
36366         if(v){
36367             this.setValue(this.fixPrecision(v));
36368         }
36369     }
36370 });/*
36371  * Based on:
36372  * Ext JS Library 1.1.1
36373  * Copyright(c) 2006-2007, Ext JS, LLC.
36374  *
36375  * Originally Released Under LGPL - original licence link has changed is not relivant.
36376  *
36377  * Fork - LGPL
36378  * <script type="text/javascript">
36379  */
36380  
36381 /**
36382  * @class Roo.form.DateField
36383  * @extends Roo.form.TriggerField
36384  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36385 * @constructor
36386 * Create a new DateField
36387 * @param {Object} config
36388  */
36389 Roo.form.DateField = function(config){
36390     Roo.form.DateField.superclass.constructor.call(this, config);
36391     
36392       this.addEvents({
36393          
36394         /**
36395          * @event select
36396          * Fires when a date is selected
36397              * @param {Roo.form.DateField} combo This combo box
36398              * @param {Date} date The date selected
36399              */
36400         'select' : true
36401          
36402     });
36403     
36404     
36405     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36406     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36407     this.ddMatch = null;
36408     if(this.disabledDates){
36409         var dd = this.disabledDates;
36410         var re = "(?:";
36411         for(var i = 0; i < dd.length; i++){
36412             re += dd[i];
36413             if(i != dd.length-1) re += "|";
36414         }
36415         this.ddMatch = new RegExp(re + ")");
36416     }
36417 };
36418
36419 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36420     /**
36421      * @cfg {String} format
36422      * The default date format string which can be overriden for localization support.  The format must be
36423      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36424      */
36425     format : "m/d/y",
36426     /**
36427      * @cfg {String} altFormats
36428      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36429      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36430      */
36431     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36432     /**
36433      * @cfg {Array} disabledDays
36434      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36435      */
36436     disabledDays : null,
36437     /**
36438      * @cfg {String} disabledDaysText
36439      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36440      */
36441     disabledDaysText : "Disabled",
36442     /**
36443      * @cfg {Array} disabledDates
36444      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36445      * expression so they are very powerful. Some examples:
36446      * <ul>
36447      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36448      * <li>["03/08", "09/16"] would disable those days for every year</li>
36449      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36450      * <li>["03/../2006"] would disable every day in March 2006</li>
36451      * <li>["^03"] would disable every day in every March</li>
36452      * </ul>
36453      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36454      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36455      */
36456     disabledDates : null,
36457     /**
36458      * @cfg {String} disabledDatesText
36459      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36460      */
36461     disabledDatesText : "Disabled",
36462     /**
36463      * @cfg {Date/String} minValue
36464      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36465      * valid format (defaults to null).
36466      */
36467     minValue : null,
36468     /**
36469      * @cfg {Date/String} maxValue
36470      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36471      * valid format (defaults to null).
36472      */
36473     maxValue : null,
36474     /**
36475      * @cfg {String} minText
36476      * The error text to display when the date in the cell is before minValue (defaults to
36477      * 'The date in this field must be after {minValue}').
36478      */
36479     minText : "The date in this field must be equal to or after {0}",
36480     /**
36481      * @cfg {String} maxText
36482      * The error text to display when the date in the cell is after maxValue (defaults to
36483      * 'The date in this field must be before {maxValue}').
36484      */
36485     maxText : "The date in this field must be equal to or before {0}",
36486     /**
36487      * @cfg {String} invalidText
36488      * The error text to display when the date in the field is invalid (defaults to
36489      * '{value} is not a valid date - it must be in the format {format}').
36490      */
36491     invalidText : "{0} is not a valid date - it must be in the format {1}",
36492     /**
36493      * @cfg {String} triggerClass
36494      * An additional CSS class used to style the trigger button.  The trigger will always get the
36495      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36496      * which displays a calendar icon).
36497      */
36498     triggerClass : 'x-form-date-trigger',
36499     
36500
36501     /**
36502      * @cfg {bool} useIso
36503      * if enabled, then the date field will use a hidden field to store the 
36504      * real value as iso formated date. default (false)
36505      */ 
36506     useIso : false,
36507     /**
36508      * @cfg {String/Object} autoCreate
36509      * A DomHelper element spec, or true for a default element spec (defaults to
36510      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36511      */ 
36512     // private
36513     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36514     
36515     // private
36516     hiddenField: false,
36517     
36518     onRender : function(ct, position)
36519     {
36520         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36521         if (this.useIso) {
36522             this.el.dom.removeAttribute('name'); 
36523             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36524                     'before', true);
36525             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36526             // prevent input submission
36527             this.hiddenName = this.name;
36528         }
36529             
36530             
36531     },
36532     
36533     // private
36534     validateValue : function(value)
36535     {
36536         value = this.formatDate(value);
36537         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36538             return false;
36539         }
36540         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36541              return true;
36542         }
36543         var svalue = value;
36544         value = this.parseDate(value);
36545         if(!value){
36546             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36547             return false;
36548         }
36549         var time = value.getTime();
36550         if(this.minValue && time < this.minValue.getTime()){
36551             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36552             return false;
36553         }
36554         if(this.maxValue && time > this.maxValue.getTime()){
36555             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36556             return false;
36557         }
36558         if(this.disabledDays){
36559             var day = value.getDay();
36560             for(var i = 0; i < this.disabledDays.length; i++) {
36561                 if(day === this.disabledDays[i]){
36562                     this.markInvalid(this.disabledDaysText);
36563                     return false;
36564                 }
36565             }
36566         }
36567         var fvalue = this.formatDate(value);
36568         if(this.ddMatch && this.ddMatch.test(fvalue)){
36569             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36570             return false;
36571         }
36572         return true;
36573     },
36574
36575     // private
36576     // Provides logic to override the default TriggerField.validateBlur which just returns true
36577     validateBlur : function(){
36578         return !this.menu || !this.menu.isVisible();
36579     },
36580
36581     /**
36582      * Returns the current date value of the date field.
36583      * @return {Date} The date value
36584      */
36585     getValue : function(){
36586         
36587         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36588     },
36589
36590     /**
36591      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36592      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36593      * (the default format used is "m/d/y").
36594      * <br />Usage:
36595      * <pre><code>
36596 //All of these calls set the same date value (May 4, 2006)
36597
36598 //Pass a date object:
36599 var dt = new Date('5/4/06');
36600 dateField.setValue(dt);
36601
36602 //Pass a date string (default format):
36603 dateField.setValue('5/4/06');
36604
36605 //Pass a date string (custom format):
36606 dateField.format = 'Y-m-d';
36607 dateField.setValue('2006-5-4');
36608 </code></pre>
36609      * @param {String/Date} date The date or valid date string
36610      */
36611     setValue : function(date){
36612         if (this.hiddenField) {
36613             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36614         }
36615         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36616     },
36617
36618     // private
36619     parseDate : function(value){
36620         if(!value || value instanceof Date){
36621             return value;
36622         }
36623         var v = Date.parseDate(value, this.format);
36624         if(!v && this.altFormats){
36625             if(!this.altFormatsArray){
36626                 this.altFormatsArray = this.altFormats.split("|");
36627             }
36628             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36629                 v = Date.parseDate(value, this.altFormatsArray[i]);
36630             }
36631         }
36632         return v;
36633     },
36634
36635     // private
36636     formatDate : function(date, fmt){
36637         return (!date || !(date instanceof Date)) ?
36638                date : date.dateFormat(fmt || this.format);
36639     },
36640
36641     // private
36642     menuListeners : {
36643         select: function(m, d){
36644             this.setValue(d);
36645             this.fireEvent('select', this, d);
36646         },
36647         show : function(){ // retain focus styling
36648             this.onFocus();
36649         },
36650         hide : function(){
36651             this.focus.defer(10, this);
36652             var ml = this.menuListeners;
36653             this.menu.un("select", ml.select,  this);
36654             this.menu.un("show", ml.show,  this);
36655             this.menu.un("hide", ml.hide,  this);
36656         }
36657     },
36658
36659     // private
36660     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36661     onTriggerClick : function(){
36662         if(this.disabled){
36663             return;
36664         }
36665         if(this.menu == null){
36666             this.menu = new Roo.menu.DateMenu();
36667         }
36668         Roo.apply(this.menu.picker,  {
36669             showClear: this.allowBlank,
36670             minDate : this.minValue,
36671             maxDate : this.maxValue,
36672             disabledDatesRE : this.ddMatch,
36673             disabledDatesText : this.disabledDatesText,
36674             disabledDays : this.disabledDays,
36675             disabledDaysText : this.disabledDaysText,
36676             format : this.format,
36677             minText : String.format(this.minText, this.formatDate(this.minValue)),
36678             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36679         });
36680         this.menu.on(Roo.apply({}, this.menuListeners, {
36681             scope:this
36682         }));
36683         this.menu.picker.setValue(this.getValue() || new Date());
36684         this.menu.show(this.el, "tl-bl?");
36685     },
36686
36687     beforeBlur : function(){
36688         var v = this.parseDate(this.getRawValue());
36689         if(v){
36690             this.setValue(v);
36691         }
36692     }
36693
36694     /** @cfg {Boolean} grow @hide */
36695     /** @cfg {Number} growMin @hide */
36696     /** @cfg {Number} growMax @hide */
36697     /**
36698      * @hide
36699      * @method autoSize
36700      */
36701 });/*
36702  * Based on:
36703  * Ext JS Library 1.1.1
36704  * Copyright(c) 2006-2007, Ext JS, LLC.
36705  *
36706  * Originally Released Under LGPL - original licence link has changed is not relivant.
36707  *
36708  * Fork - LGPL
36709  * <script type="text/javascript">
36710  */
36711  
36712
36713 /**
36714  * @class Roo.form.ComboBox
36715  * @extends Roo.form.TriggerField
36716  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36717  * @constructor
36718  * Create a new ComboBox.
36719  * @param {Object} config Configuration options
36720  */
36721 Roo.form.ComboBox = function(config){
36722     Roo.form.ComboBox.superclass.constructor.call(this, config);
36723     this.addEvents({
36724         /**
36725          * @event expand
36726          * Fires when the dropdown list is expanded
36727              * @param {Roo.form.ComboBox} combo This combo box
36728              */
36729         'expand' : true,
36730         /**
36731          * @event collapse
36732          * Fires when the dropdown list is collapsed
36733              * @param {Roo.form.ComboBox} combo This combo box
36734              */
36735         'collapse' : true,
36736         /**
36737          * @event beforeselect
36738          * Fires before a list item is selected. Return false to cancel the selection.
36739              * @param {Roo.form.ComboBox} combo This combo box
36740              * @param {Roo.data.Record} record The data record returned from the underlying store
36741              * @param {Number} index The index of the selected item in the dropdown list
36742              */
36743         'beforeselect' : true,
36744         /**
36745          * @event select
36746          * Fires when a list item is selected
36747              * @param {Roo.form.ComboBox} combo This combo box
36748              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36749              * @param {Number} index The index of the selected item in the dropdown list
36750              */
36751         'select' : true,
36752         /**
36753          * @event beforequery
36754          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36755          * The event object passed has these properties:
36756              * @param {Roo.form.ComboBox} combo This combo box
36757              * @param {String} query The query
36758              * @param {Boolean} forceAll true to force "all" query
36759              * @param {Boolean} cancel true to cancel the query
36760              * @param {Object} e The query event object
36761              */
36762         'beforequery': true,
36763          /**
36764          * @event add
36765          * Fires when the 'add' icon is pressed (add a listener to enable add button)
36766              * @param {Roo.form.ComboBox} combo This combo box
36767              */
36768         'add' : true,
36769         /**
36770          * @event edit
36771          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
36772              * @param {Roo.form.ComboBox} combo This combo box
36773              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36774              */
36775         'edit' : true
36776         
36777         
36778     });
36779     if(this.transform){
36780         this.allowDomMove = false;
36781         var s = Roo.getDom(this.transform);
36782         if(!this.hiddenName){
36783             this.hiddenName = s.name;
36784         }
36785         if(!this.store){
36786             this.mode = 'local';
36787             var d = [], opts = s.options;
36788             for(var i = 0, len = opts.length;i < len; i++){
36789                 var o = opts[i];
36790                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36791                 if(o.selected) {
36792                     this.value = value;
36793                 }
36794                 d.push([value, o.text]);
36795             }
36796             this.store = new Roo.data.SimpleStore({
36797                 'id': 0,
36798                 fields: ['value', 'text'],
36799                 data : d
36800             });
36801             this.valueField = 'value';
36802             this.displayField = 'text';
36803         }
36804         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36805         if(!this.lazyRender){
36806             this.target = true;
36807             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36808             s.parentNode.removeChild(s); // remove it
36809             this.render(this.el.parentNode);
36810         }else{
36811             s.parentNode.removeChild(s); // remove it
36812         }
36813
36814     }
36815     if (this.store) {
36816         this.store = Roo.factory(this.store, Roo.data);
36817     }
36818     
36819     this.selectedIndex = -1;
36820     if(this.mode == 'local'){
36821         if(config.queryDelay === undefined){
36822             this.queryDelay = 10;
36823         }
36824         if(config.minChars === undefined){
36825             this.minChars = 0;
36826         }
36827     }
36828 };
36829
36830 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36831     /**
36832      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36833      */
36834     /**
36835      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36836      * rendering into an Roo.Editor, defaults to false)
36837      */
36838     /**
36839      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36840      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36841      */
36842     /**
36843      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36844      */
36845     /**
36846      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36847      * the dropdown list (defaults to undefined, with no header element)
36848      */
36849
36850      /**
36851      * @cfg {String/Roo.Template} tpl The template to use to render the output
36852      */
36853      
36854     // private
36855     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36856     /**
36857      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36858      */
36859     listWidth: undefined,
36860     /**
36861      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36862      * mode = 'remote' or 'text' if mode = 'local')
36863      */
36864     displayField: undefined,
36865     /**
36866      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36867      * mode = 'remote' or 'value' if mode = 'local'). 
36868      * Note: use of a valueField requires the user make a selection
36869      * in order for a value to be mapped.
36870      */
36871     valueField: undefined,
36872     /**
36873      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36874      * field's data value (defaults to the underlying DOM element's name)
36875      */
36876     hiddenName: undefined,
36877     /**
36878      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36879      */
36880     listClass: '',
36881     /**
36882      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36883      */
36884     selectedClass: 'x-combo-selected',
36885     /**
36886      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36887      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36888      * which displays a downward arrow icon).
36889      */
36890     triggerClass : 'x-form-arrow-trigger',
36891     /**
36892      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36893      */
36894     shadow:'sides',
36895     /**
36896      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36897      * anchor positions (defaults to 'tl-bl')
36898      */
36899     listAlign: 'tl-bl?',
36900     /**
36901      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36902      */
36903     maxHeight: 300,
36904     /**
36905      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36906      * query specified by the allQuery config option (defaults to 'query')
36907      */
36908     triggerAction: 'query',
36909     /**
36910      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36911      * (defaults to 4, does not apply if editable = false)
36912      */
36913     minChars : 4,
36914     /**
36915      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36916      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36917      */
36918     typeAhead: false,
36919     /**
36920      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36921      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36922      */
36923     queryDelay: 500,
36924     /**
36925      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36926      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36927      */
36928     pageSize: 0,
36929     /**
36930      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36931      * when editable = true (defaults to false)
36932      */
36933     selectOnFocus:false,
36934     /**
36935      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36936      */
36937     queryParam: 'query',
36938     /**
36939      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36940      * when mode = 'remote' (defaults to 'Loading...')
36941      */
36942     loadingText: 'Loading...',
36943     /**
36944      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36945      */
36946     resizable: false,
36947     /**
36948      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36949      */
36950     handleHeight : 8,
36951     /**
36952      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36953      * traditional select (defaults to true)
36954      */
36955     editable: true,
36956     /**
36957      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36958      */
36959     allQuery: '',
36960     /**
36961      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36962      */
36963     mode: 'remote',
36964     /**
36965      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36966      * listWidth has a higher value)
36967      */
36968     minListWidth : 70,
36969     /**
36970      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36971      * allow the user to set arbitrary text into the field (defaults to false)
36972      */
36973     forceSelection:false,
36974     /**
36975      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36976      * if typeAhead = true (defaults to 250)
36977      */
36978     typeAheadDelay : 250,
36979     /**
36980      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36981      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36982      */
36983     valueNotFoundText : undefined,
36984     /**
36985      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36986      */
36987     blockFocus : false,
36988     
36989     /**
36990      * @cfg {bool} disableClear Disable showing of clear button.
36991      */
36992     disableClear : false,
36993     
36994     //private
36995     addicon : false,
36996     editicon: false,
36997     
36998     
36999     // private
37000     onRender : function(ct, position){
37001         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37002         if(this.hiddenName){
37003             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37004                     'before', true);
37005             this.hiddenField.value =
37006                 this.hiddenValue !== undefined ? this.hiddenValue :
37007                 this.value !== undefined ? this.value : '';
37008
37009             // prevent input submission
37010             this.el.dom.removeAttribute('name');
37011         }
37012         if(Roo.isGecko){
37013             this.el.dom.setAttribute('autocomplete', 'off');
37014         }
37015
37016         var cls = 'x-combo-list';
37017
37018         this.list = new Roo.Layer({
37019             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37020         });
37021
37022         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37023         this.list.setWidth(lw);
37024         this.list.swallowEvent('mousewheel');
37025         this.assetHeight = 0;
37026
37027         if(this.title){
37028             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37029             this.assetHeight += this.header.getHeight();
37030         }
37031
37032         this.innerList = this.list.createChild({cls:cls+'-inner'});
37033         this.innerList.on('mouseover', this.onViewOver, this);
37034         this.innerList.on('mousemove', this.onViewMove, this);
37035         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37036         
37037         if(this.allowBlank && !this.pageSize && !this.disableClear){
37038             this.footer = this.list.createChild({cls:cls+'-ft'});
37039             this.pageTb = new Roo.Toolbar(this.footer);
37040            
37041         }
37042         if(this.pageSize){
37043             this.footer = this.list.createChild({cls:cls+'-ft'});
37044             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37045                     {pageSize: this.pageSize});
37046             
37047         }
37048         
37049         if (this.pageTb && this.allowBlank && !this.disableClear) {
37050             var _this = this;
37051             this.pageTb.add(new Roo.Toolbar.Fill(), {
37052                 cls: 'x-btn-icon x-btn-clear',
37053                 text: '&#160;',
37054                 handler: function()
37055                 {
37056                     _this.collapse();
37057                     _this.clearValue();
37058                     _this.onSelect(false, -1);
37059                 }
37060             });
37061         }
37062         if (this.footer) {
37063             this.assetHeight += this.footer.getHeight();
37064         }
37065         
37066
37067         if(!this.tpl){
37068             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37069         }
37070
37071         this.view = new Roo.View(this.innerList, this.tpl, {
37072             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37073         });
37074
37075         this.view.on('click', this.onViewClick, this);
37076
37077         this.store.on('beforeload', this.onBeforeLoad, this);
37078         this.store.on('load', this.onLoad, this);
37079         this.store.on('loadexception', this.collapse, this);
37080
37081         if(this.resizable){
37082             this.resizer = new Roo.Resizable(this.list,  {
37083                pinned:true, handles:'se'
37084             });
37085             this.resizer.on('resize', function(r, w, h){
37086                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37087                 this.listWidth = w;
37088                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37089                 this.restrictHeight();
37090             }, this);
37091             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37092         }
37093         if(!this.editable){
37094             this.editable = true;
37095             this.setEditable(false);
37096         }  
37097         
37098         
37099         if (typeof(this.events.add.listeners) != 'undefined') {
37100             
37101             this.addicon = this.wrap.createChild(
37102                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37103        
37104             this.addicon.on('click', function(e) {
37105                 this.fireEvent('add', this);
37106             }, this);
37107         }
37108         if (typeof(this.events.edit.listeners) != 'undefined') {
37109             
37110             this.editicon = this.wrap.createChild(
37111                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37112             if (this.addicon) {
37113                 this.editicon.setStyle('margin-left', '40px');
37114             }
37115             this.editicon.on('click', function(e) {
37116                 
37117                 // we fire even  if inothing is selected..
37118                 this.fireEvent('edit', this, this.lastData );
37119                 
37120             }, this);
37121         }
37122         
37123         
37124         
37125     },
37126
37127     // private
37128     initEvents : function(){
37129         Roo.form.ComboBox.superclass.initEvents.call(this);
37130
37131         this.keyNav = new Roo.KeyNav(this.el, {
37132             "up" : function(e){
37133                 this.inKeyMode = true;
37134                 this.selectPrev();
37135             },
37136
37137             "down" : function(e){
37138                 if(!this.isExpanded()){
37139                     this.onTriggerClick();
37140                 }else{
37141                     this.inKeyMode = true;
37142                     this.selectNext();
37143                 }
37144             },
37145
37146             "enter" : function(e){
37147                 this.onViewClick();
37148                 //return true;
37149             },
37150
37151             "esc" : function(e){
37152                 this.collapse();
37153             },
37154
37155             "tab" : function(e){
37156                 this.onViewClick(false);
37157                 return true;
37158             },
37159
37160             scope : this,
37161
37162             doRelay : function(foo, bar, hname){
37163                 if(hname == 'down' || this.scope.isExpanded()){
37164                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37165                 }
37166                 return true;
37167             },
37168
37169             forceKeyDown: true
37170         });
37171         this.queryDelay = Math.max(this.queryDelay || 10,
37172                 this.mode == 'local' ? 10 : 250);
37173         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37174         if(this.typeAhead){
37175             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37176         }
37177         if(this.editable !== false){
37178             this.el.on("keyup", this.onKeyUp, this);
37179         }
37180         if(this.forceSelection){
37181             this.on('blur', this.doForce, this);
37182         }
37183     },
37184
37185     onDestroy : function(){
37186         if(this.view){
37187             this.view.setStore(null);
37188             this.view.el.removeAllListeners();
37189             this.view.el.remove();
37190             this.view.purgeListeners();
37191         }
37192         if(this.list){
37193             this.list.destroy();
37194         }
37195         if(this.store){
37196             this.store.un('beforeload', this.onBeforeLoad, this);
37197             this.store.un('load', this.onLoad, this);
37198             this.store.un('loadexception', this.collapse, this);
37199         }
37200         Roo.form.ComboBox.superclass.onDestroy.call(this);
37201     },
37202
37203     // private
37204     fireKey : function(e){
37205         if(e.isNavKeyPress() && !this.list.isVisible()){
37206             this.fireEvent("specialkey", this, e);
37207         }
37208     },
37209
37210     // private
37211     onResize: function(w, h){
37212         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37213         
37214         if(typeof w != 'number'){
37215             // we do not handle it!?!?
37216             return;
37217         }
37218         var tw = this.trigger.getWidth();
37219         tw += this.addicon ? this.addicon.getWidth() : 0;
37220         tw += this.editicon ? this.editicon.getWidth() : 0;
37221         var x = w - tw;
37222         this.el.setWidth( this.adjustWidth('input', x));
37223             
37224         this.trigger.setStyle('left', x+'px');
37225         
37226         if(this.list && this.listWidth === undefined){
37227             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37228             this.list.setWidth(lw);
37229             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37230         }
37231         
37232     
37233         
37234     },
37235
37236     /**
37237      * Allow or prevent the user from directly editing the field text.  If false is passed,
37238      * the user will only be able to select from the items defined in the dropdown list.  This method
37239      * is the runtime equivalent of setting the 'editable' config option at config time.
37240      * @param {Boolean} value True to allow the user to directly edit the field text
37241      */
37242     setEditable : function(value){
37243         if(value == this.editable){
37244             return;
37245         }
37246         this.editable = value;
37247         if(!value){
37248             this.el.dom.setAttribute('readOnly', true);
37249             this.el.on('mousedown', this.onTriggerClick,  this);
37250             this.el.addClass('x-combo-noedit');
37251         }else{
37252             this.el.dom.setAttribute('readOnly', false);
37253             this.el.un('mousedown', this.onTriggerClick,  this);
37254             this.el.removeClass('x-combo-noedit');
37255         }
37256     },
37257
37258     // private
37259     onBeforeLoad : function(){
37260         if(!this.hasFocus){
37261             return;
37262         }
37263         this.innerList.update(this.loadingText ?
37264                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37265         this.restrictHeight();
37266         this.selectedIndex = -1;
37267     },
37268
37269     // private
37270     onLoad : function(){
37271         if(!this.hasFocus){
37272             return;
37273         }
37274         if(this.store.getCount() > 0){
37275             this.expand();
37276             this.restrictHeight();
37277             if(this.lastQuery == this.allQuery){
37278                 if(this.editable){
37279                     this.el.dom.select();
37280                 }
37281                 if(!this.selectByValue(this.value, true)){
37282                     this.select(0, true);
37283                 }
37284             }else{
37285                 this.selectNext();
37286                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37287                     this.taTask.delay(this.typeAheadDelay);
37288                 }
37289             }
37290         }else{
37291             this.onEmptyResults();
37292         }
37293         //this.el.focus();
37294     },
37295
37296     // private
37297     onTypeAhead : function(){
37298         if(this.store.getCount() > 0){
37299             var r = this.store.getAt(0);
37300             var newValue = r.data[this.displayField];
37301             var len = newValue.length;
37302             var selStart = this.getRawValue().length;
37303             if(selStart != len){
37304                 this.setRawValue(newValue);
37305                 this.selectText(selStart, newValue.length);
37306             }
37307         }
37308     },
37309
37310     // private
37311     onSelect : function(record, index){
37312         if(this.fireEvent('beforeselect', this, record, index) !== false){
37313             this.setFromData(index > -1 ? record.data : false);
37314             this.collapse();
37315             this.fireEvent('select', this, record, index);
37316         }
37317     },
37318
37319     /**
37320      * Returns the currently selected field value or empty string if no value is set.
37321      * @return {String} value The selected value
37322      */
37323     getValue : function(){
37324         if(this.valueField){
37325             return typeof this.value != 'undefined' ? this.value : '';
37326         }else{
37327             return Roo.form.ComboBox.superclass.getValue.call(this);
37328         }
37329     },
37330
37331     /**
37332      * Clears any text/value currently set in the field
37333      */
37334     clearValue : function(){
37335         if(this.hiddenField){
37336             this.hiddenField.value = '';
37337         }
37338         this.value = '';
37339         this.setRawValue('');
37340         this.lastSelectionText = '';
37341         this.applyEmptyText();
37342     },
37343
37344     /**
37345      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37346      * will be displayed in the field.  If the value does not match the data value of an existing item,
37347      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37348      * Otherwise the field will be blank (although the value will still be set).
37349      * @param {String} value The value to match
37350      */
37351     setValue : function(v){
37352         var text = v;
37353         if(this.valueField){
37354             var r = this.findRecord(this.valueField, v);
37355             if(r){
37356                 text = r.data[this.displayField];
37357             }else if(this.valueNotFoundText !== undefined){
37358                 text = this.valueNotFoundText;
37359             }
37360         }
37361         this.lastSelectionText = text;
37362         if(this.hiddenField){
37363             this.hiddenField.value = v;
37364         }
37365         Roo.form.ComboBox.superclass.setValue.call(this, text);
37366         this.value = v;
37367     },
37368     /**
37369      * @property {Object} the last set data for the element
37370      */
37371     
37372     lastData : false,
37373     /**
37374      * Sets the value of the field based on a object which is related to the record format for the store.
37375      * @param {Object} value the value to set as. or false on reset?
37376      */
37377     setFromData : function(o){
37378         var dv = ''; // display value
37379         var vv = ''; // value value..
37380         this.lastData = o;
37381         if (this.displayField) {
37382             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37383         } else {
37384             // this is an error condition!!!
37385             console.log('no value field set for '+ this.name);
37386         }
37387         
37388         if(this.valueField){
37389             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37390         }
37391         if(this.hiddenField){
37392             this.hiddenField.value = vv;
37393             
37394             this.lastSelectionText = dv;
37395             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37396             this.value = vv;
37397             return;
37398         }
37399         // no hidden field.. - we store the value in 'value', but still display
37400         // display field!!!!
37401         this.lastSelectionText = dv;
37402         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37403         this.value = vv;
37404         
37405         
37406     },
37407     // private
37408     reset : function(){
37409         // overridden so that last data is reset..
37410         this.setValue(this.originalValue);
37411         this.clearInvalid();
37412         this.lastData = false;
37413     },
37414     // private
37415     findRecord : function(prop, value){
37416         var record;
37417         if(this.store.getCount() > 0){
37418             this.store.each(function(r){
37419                 if(r.data[prop] == value){
37420                     record = r;
37421                     return false;
37422                 }
37423             });
37424         }
37425         return record;
37426     },
37427
37428     // private
37429     onViewMove : function(e, t){
37430         this.inKeyMode = false;
37431     },
37432
37433     // private
37434     onViewOver : function(e, t){
37435         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37436             return;
37437         }
37438         var item = this.view.findItemFromChild(t);
37439         if(item){
37440             var index = this.view.indexOf(item);
37441             this.select(index, false);
37442         }
37443     },
37444
37445     // private
37446     onViewClick : function(doFocus){
37447         var index = this.view.getSelectedIndexes()[0];
37448         var r = this.store.getAt(index);
37449         if(r){
37450             this.onSelect(r, index);
37451         }
37452         if(doFocus !== false && !this.blockFocus){
37453             this.el.focus();
37454         }
37455     },
37456
37457     // private
37458     restrictHeight : function(){
37459         this.innerList.dom.style.height = '';
37460         var inner = this.innerList.dom;
37461         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37462         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37463         this.list.beginUpdate();
37464         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37465         this.list.alignTo(this.el, this.listAlign);
37466         this.list.endUpdate();
37467     },
37468
37469     // private
37470     onEmptyResults : function(){
37471         this.collapse();
37472     },
37473
37474     /**
37475      * Returns true if the dropdown list is expanded, else false.
37476      */
37477     isExpanded : function(){
37478         return this.list.isVisible();
37479     },
37480
37481     /**
37482      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37483      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37484      * @param {String} value The data value of the item to select
37485      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37486      * selected item if it is not currently in view (defaults to true)
37487      * @return {Boolean} True if the value matched an item in the list, else false
37488      */
37489     selectByValue : function(v, scrollIntoView){
37490         if(v !== undefined && v !== null){
37491             var r = this.findRecord(this.valueField || this.displayField, v);
37492             if(r){
37493                 this.select(this.store.indexOf(r), scrollIntoView);
37494                 return true;
37495             }
37496         }
37497         return false;
37498     },
37499
37500     /**
37501      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37502      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37503      * @param {Number} index The zero-based index of the list item to select
37504      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37505      * selected item if it is not currently in view (defaults to true)
37506      */
37507     select : function(index, scrollIntoView){
37508         this.selectedIndex = index;
37509         this.view.select(index);
37510         if(scrollIntoView !== false){
37511             var el = this.view.getNode(index);
37512             if(el){
37513                 this.innerList.scrollChildIntoView(el, false);
37514             }
37515         }
37516     },
37517
37518     // private
37519     selectNext : function(){
37520         var ct = this.store.getCount();
37521         if(ct > 0){
37522             if(this.selectedIndex == -1){
37523                 this.select(0);
37524             }else if(this.selectedIndex < ct-1){
37525                 this.select(this.selectedIndex+1);
37526             }
37527         }
37528     },
37529
37530     // private
37531     selectPrev : function(){
37532         var ct = this.store.getCount();
37533         if(ct > 0){
37534             if(this.selectedIndex == -1){
37535                 this.select(0);
37536             }else if(this.selectedIndex != 0){
37537                 this.select(this.selectedIndex-1);
37538             }
37539         }
37540     },
37541
37542     // private
37543     onKeyUp : function(e){
37544         if(this.editable !== false && !e.isSpecialKey()){
37545             this.lastKey = e.getKey();
37546             this.dqTask.delay(this.queryDelay);
37547         }
37548     },
37549
37550     // private
37551     validateBlur : function(){
37552         return !this.list || !this.list.isVisible();   
37553     },
37554
37555     // private
37556     initQuery : function(){
37557         this.doQuery(this.getRawValue());
37558     },
37559
37560     // private
37561     doForce : function(){
37562         if(this.el.dom.value.length > 0){
37563             this.el.dom.value =
37564                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37565             this.applyEmptyText();
37566         }
37567     },
37568
37569     /**
37570      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37571      * query allowing the query action to be canceled if needed.
37572      * @param {String} query The SQL query to execute
37573      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37574      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37575      * saved in the current store (defaults to false)
37576      */
37577     doQuery : function(q, forceAll){
37578         if(q === undefined || q === null){
37579             q = '';
37580         }
37581         var qe = {
37582             query: q,
37583             forceAll: forceAll,
37584             combo: this,
37585             cancel:false
37586         };
37587         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37588             return false;
37589         }
37590         q = qe.query;
37591         forceAll = qe.forceAll;
37592         if(forceAll === true || (q.length >= this.minChars)){
37593             if(this.lastQuery != q){
37594                 this.lastQuery = q;
37595                 if(this.mode == 'local'){
37596                     this.selectedIndex = -1;
37597                     if(forceAll){
37598                         this.store.clearFilter();
37599                     }else{
37600                         this.store.filter(this.displayField, q);
37601                     }
37602                     this.onLoad();
37603                 }else{
37604                     this.store.baseParams[this.queryParam] = q;
37605                     this.store.load({
37606                         params: this.getParams(q)
37607                     });
37608                     this.expand();
37609                 }
37610             }else{
37611                 this.selectedIndex = -1;
37612                 this.onLoad();   
37613             }
37614         }
37615     },
37616
37617     // private
37618     getParams : function(q){
37619         var p = {};
37620         //p[this.queryParam] = q;
37621         if(this.pageSize){
37622             p.start = 0;
37623             p.limit = this.pageSize;
37624         }
37625         return p;
37626     },
37627
37628     /**
37629      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37630      */
37631     collapse : function(){
37632         if(!this.isExpanded()){
37633             return;
37634         }
37635         this.list.hide();
37636         Roo.get(document).un('mousedown', this.collapseIf, this);
37637         Roo.get(document).un('mousewheel', this.collapseIf, this);
37638         this.fireEvent('collapse', this);
37639     },
37640
37641     // private
37642     collapseIf : function(e){
37643         if(!e.within(this.wrap) && !e.within(this.list)){
37644             this.collapse();
37645         }
37646     },
37647
37648     /**
37649      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37650      */
37651     expand : function(){
37652         if(this.isExpanded() || !this.hasFocus){
37653             return;
37654         }
37655         this.list.alignTo(this.el, this.listAlign);
37656         this.list.show();
37657         Roo.get(document).on('mousedown', this.collapseIf, this);
37658         Roo.get(document).on('mousewheel', this.collapseIf, this);
37659         this.fireEvent('expand', this);
37660     },
37661
37662     // private
37663     // Implements the default empty TriggerField.onTriggerClick function
37664     onTriggerClick : function(){
37665         if(this.disabled){
37666             return;
37667         }
37668         if(this.isExpanded()){
37669             this.collapse();
37670             if (!this.blockFocus) {
37671                 this.el.focus();
37672             }
37673             
37674         }else {
37675             this.hasFocus = true;
37676             if(this.triggerAction == 'all') {
37677                 this.doQuery(this.allQuery, true);
37678             } else {
37679                 this.doQuery(this.getRawValue());
37680             }
37681             if (!this.blockFocus) {
37682                 this.el.focus();
37683             }
37684         }
37685     }
37686
37687     /** 
37688     * @cfg {Boolean} grow 
37689     * @hide 
37690     */
37691     /** 
37692     * @cfg {Number} growMin 
37693     * @hide 
37694     */
37695     /** 
37696     * @cfg {Number} growMax 
37697     * @hide 
37698     */
37699     /**
37700      * @hide
37701      * @method autoSize
37702      */
37703 });/*
37704  * Based on:
37705  * Ext JS Library 1.1.1
37706  * Copyright(c) 2006-2007, Ext JS, LLC.
37707  *
37708  * Originally Released Under LGPL - original licence link has changed is not relivant.
37709  *
37710  * Fork - LGPL
37711  * <script type="text/javascript">
37712  */
37713 /**
37714  * @class Roo.form.Checkbox
37715  * @extends Roo.form.Field
37716  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37717  * @constructor
37718  * Creates a new Checkbox
37719  * @param {Object} config Configuration options
37720  */
37721 Roo.form.Checkbox = function(config){
37722     Roo.form.Checkbox.superclass.constructor.call(this, config);
37723     this.addEvents({
37724         /**
37725          * @event check
37726          * Fires when the checkbox is checked or unchecked.
37727              * @param {Roo.form.Checkbox} this This checkbox
37728              * @param {Boolean} checked The new checked value
37729              */
37730         check : true
37731     });
37732 };
37733
37734 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37735     /**
37736      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37737      */
37738     focusClass : undefined,
37739     /**
37740      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37741      */
37742     fieldClass: "x-form-field",
37743     /**
37744      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37745      */
37746     checked: false,
37747     /**
37748      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37749      * {tag: "input", type: "checkbox", autocomplete: "off"})
37750      */
37751     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37752     /**
37753      * @cfg {String} boxLabel The text that appears beside the checkbox
37754      */
37755     boxLabel : "",
37756     /**
37757      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37758      */  
37759     inputValue : '1',
37760     /**
37761      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37762      */
37763      valueOff: '0', // value when not checked..
37764
37765     actionMode : 'viewEl', 
37766     //
37767     // private
37768     itemCls : 'x-menu-check-item x-form-item',
37769     groupClass : 'x-menu-group-item',
37770     inputType : 'hidden',
37771     
37772     
37773     inSetChecked: false, // check that we are not calling self...
37774     
37775     inputElement: false, // real input element?
37776     basedOn: false, // ????
37777     
37778     isFormField: true, // not sure where this is needed!!!!
37779
37780     onResize : function(){
37781         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37782         if(!this.boxLabel){
37783             this.el.alignTo(this.wrap, 'c-c');
37784         }
37785     },
37786
37787     initEvents : function(){
37788         Roo.form.Checkbox.superclass.initEvents.call(this);
37789         this.el.on("click", this.onClick,  this);
37790         this.el.on("change", this.onClick,  this);
37791     },
37792
37793
37794     getResizeEl : function(){
37795         return this.wrap;
37796     },
37797
37798     getPositionEl : function(){
37799         return this.wrap;
37800     },
37801
37802     // private
37803     onRender : function(ct, position){
37804         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37805         /*
37806         if(this.inputValue !== undefined){
37807             this.el.dom.value = this.inputValue;
37808         }
37809         */
37810         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37811         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37812         var viewEl = this.wrap.createChild({ 
37813             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37814         this.viewEl = viewEl;   
37815         this.wrap.on('click', this.onClick,  this); 
37816         
37817         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37818         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37819         
37820         
37821         
37822         if(this.boxLabel){
37823             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37824         //    viewEl.on('click', this.onClick,  this); 
37825         }
37826         //if(this.checked){
37827             this.setChecked(this.checked);
37828         //}else{
37829             //this.checked = this.el.dom;
37830         //}
37831
37832     },
37833
37834     // private
37835     initValue : Roo.emptyFn,
37836
37837     /**
37838      * Returns the checked state of the checkbox.
37839      * @return {Boolean} True if checked, else false
37840      */
37841     getValue : function(){
37842         if(this.el){
37843             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37844         }
37845         return this.valueOff;
37846         
37847     },
37848
37849         // private
37850     onClick : function(){ 
37851         this.setChecked(!this.checked);
37852
37853         //if(this.el.dom.checked != this.checked){
37854         //    this.setValue(this.el.dom.checked);
37855        // }
37856     },
37857
37858     /**
37859      * Sets the checked state of the checkbox.
37860      * On is always based on a string comparison between inputValue and the param.
37861      * @param {Boolean/String} value - the value to set 
37862      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
37863      */
37864     setValue : function(v,suppressEvent){
37865         
37866         
37867         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37868         //if(this.el && this.el.dom){
37869         //    this.el.dom.checked = this.checked;
37870         //    this.el.dom.defaultChecked = this.checked;
37871         //}
37872         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
37873         //this.fireEvent("check", this, this.checked);
37874     },
37875     // private..
37876     setChecked : function(state,suppressEvent)
37877     {
37878         if (this.inSetChecked) {
37879             this.checked = state;
37880             return;
37881         }
37882         
37883     
37884         if(this.wrap){
37885             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37886         }
37887         this.checked = state;
37888         if(suppressEvent !== true){
37889             this.fireEvent('checkchange', this, state);
37890         }
37891         this.inSetChecked = true;
37892         this.el.dom.value = state ? this.inputValue : this.valueOff;
37893         this.inSetChecked = false;
37894         
37895     },
37896     // handle setting of hidden value by some other method!!?!?
37897     setFromHidden: function()
37898     {
37899         if(!this.el){
37900             return;
37901         }
37902         //console.log("SET FROM HIDDEN");
37903         //alert('setFrom hidden');
37904         this.setValue(this.el.dom.value);
37905     },
37906     
37907     onDestroy : function()
37908     {
37909         if(this.viewEl){
37910             Roo.get(this.viewEl).remove();
37911         }
37912          
37913         Roo.form.Checkbox.superclass.onDestroy.call(this);
37914     }
37915
37916 });/*
37917  * Based on:
37918  * Ext JS Library 1.1.1
37919  * Copyright(c) 2006-2007, Ext JS, LLC.
37920  *
37921  * Originally Released Under LGPL - original licence link has changed is not relivant.
37922  *
37923  * Fork - LGPL
37924  * <script type="text/javascript">
37925  */
37926  
37927 /**
37928  * @class Roo.form.Radio
37929  * @extends Roo.form.Checkbox
37930  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37931  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37932  * @constructor
37933  * Creates a new Radio
37934  * @param {Object} config Configuration options
37935  */
37936 Roo.form.Radio = function(){
37937     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37938 };
37939 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37940     inputType: 'radio',
37941
37942     /**
37943      * If this radio is part of a group, it will return the selected value
37944      * @return {String}
37945      */
37946     getGroupValue : function(){
37947         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37948     }
37949 });//<script type="text/javascript">
37950
37951 /*
37952  * Ext JS Library 1.1.1
37953  * Copyright(c) 2006-2007, Ext JS, LLC.
37954  * licensing@extjs.com
37955  * 
37956  * http://www.extjs.com/license
37957  */
37958  
37959  /*
37960   * 
37961   * Known bugs:
37962   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37963   * - IE ? - no idea how much works there.
37964   * 
37965   * 
37966   * 
37967   */
37968  
37969
37970 /**
37971  * @class Ext.form.HtmlEditor
37972  * @extends Ext.form.Field
37973  * Provides a lightweight HTML Editor component.
37974  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37975  * 
37976  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37977  * supported by this editor.</b><br/><br/>
37978  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37979  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37980  */
37981 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37982       /**
37983      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37984      */
37985     toolbars : false,
37986     /**
37987      * @cfg {String} createLinkText The default text for the create link prompt
37988      */
37989     createLinkText : 'Please enter the URL for the link:',
37990     /**
37991      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37992      */
37993     defaultLinkValue : 'http:/'+'/',
37994    
37995     
37996     // id of frame..
37997     frameId: false,
37998     
37999     // private properties
38000     validationEvent : false,
38001     deferHeight: true,
38002     initialized : false,
38003     activated : false,
38004     sourceEditMode : false,
38005     onFocus : Roo.emptyFn,
38006     iframePad:3,
38007     hideMode:'offsets',
38008     defaultAutoCreate : {
38009         tag: "textarea",
38010         style:"width:500px;height:300px;",
38011         autocomplete: "off"
38012     },
38013
38014     // private
38015     initComponent : function(){
38016         this.addEvents({
38017             /**
38018              * @event initialize
38019              * Fires when the editor is fully initialized (including the iframe)
38020              * @param {HtmlEditor} this
38021              */
38022             initialize: true,
38023             /**
38024              * @event activate
38025              * Fires when the editor is first receives the focus. Any insertion must wait
38026              * until after this event.
38027              * @param {HtmlEditor} this
38028              */
38029             activate: true,
38030              /**
38031              * @event beforesync
38032              * Fires before the textarea is updated with content from the editor iframe. Return false
38033              * to cancel the sync.
38034              * @param {HtmlEditor} this
38035              * @param {String} html
38036              */
38037             beforesync: true,
38038              /**
38039              * @event beforepush
38040              * Fires before the iframe editor is updated with content from the textarea. Return false
38041              * to cancel the push.
38042              * @param {HtmlEditor} this
38043              * @param {String} html
38044              */
38045             beforepush: true,
38046              /**
38047              * @event sync
38048              * Fires when the textarea is updated with content from the editor iframe.
38049              * @param {HtmlEditor} this
38050              * @param {String} html
38051              */
38052             sync: true,
38053              /**
38054              * @event push
38055              * Fires when the iframe editor is updated with content from the textarea.
38056              * @param {HtmlEditor} this
38057              * @param {String} html
38058              */
38059             push: true,
38060              /**
38061              * @event editmodechange
38062              * Fires when the editor switches edit modes
38063              * @param {HtmlEditor} this
38064              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38065              */
38066             editmodechange: true,
38067             /**
38068              * @event editorevent
38069              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38070              * @param {HtmlEditor} this
38071              */
38072             editorevent: true
38073         })
38074     },
38075
38076     /**
38077      * Protected method that will not generally be called directly. It
38078      * is called when the editor creates its toolbar. Override this method if you need to
38079      * add custom toolbar buttons.
38080      * @param {HtmlEditor} editor
38081      */
38082     createToolbar : function(editor){
38083         if (!editor.toolbars || !editor.toolbars.length) {
38084             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38085         }
38086         
38087         for (var i =0 ; i < editor.toolbars.length;i++) {
38088             editor.toolbars[i].init(editor);
38089         }
38090          
38091         
38092     },
38093
38094     /**
38095      * Protected method that will not generally be called directly. It
38096      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38097      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38098      */
38099     getDocMarkup : function(){
38100         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38101     },
38102
38103     // private
38104     onRender : function(ct, position){
38105         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38106         this.el.dom.style.border = '0 none';
38107         this.el.dom.setAttribute('tabIndex', -1);
38108         this.el.addClass('x-hidden');
38109         if(Roo.isIE){ // fix IE 1px bogus margin
38110             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38111         }
38112         this.wrap = this.el.wrap({
38113             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38114         });
38115
38116         this.frameId = Roo.id();
38117         this.createToolbar(this);
38118         
38119         
38120         
38121         
38122       
38123         
38124         var iframe = this.wrap.createChild({
38125             tag: 'iframe',
38126             id: this.frameId,
38127             name: this.frameId,
38128             frameBorder : 'no',
38129             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38130         });
38131         
38132        // console.log(iframe);
38133         //this.wrap.dom.appendChild(iframe);
38134
38135         this.iframe = iframe.dom;
38136
38137          this.assignDocWin();
38138         
38139         this.doc.designMode = 'on';
38140        
38141         this.doc.open();
38142         this.doc.write(this.getDocMarkup());
38143         this.doc.close();
38144
38145         
38146         var task = { // must defer to wait for browser to be ready
38147             run : function(){
38148                 //console.log("run task?" + this.doc.readyState);
38149                 this.assignDocWin();
38150                 if(this.doc.body || this.doc.readyState == 'complete'){
38151                     try {
38152                         this.doc.designMode="on";
38153                     } catch (e) {
38154                         return;
38155                     }
38156                     Roo.TaskMgr.stop(task);
38157                     this.initEditor.defer(10, this);
38158                 }
38159             },
38160             interval : 10,
38161             duration:10000,
38162             scope: this
38163         };
38164         Roo.TaskMgr.start(task);
38165
38166         if(!this.width){
38167             this.setSize(this.el.getSize());
38168         }
38169     },
38170
38171     // private
38172     onResize : function(w, h){
38173         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38174         if(this.el && this.iframe){
38175             if(typeof w == 'number'){
38176                 var aw = w - this.wrap.getFrameWidth('lr');
38177                 this.el.setWidth(this.adjustWidth('textarea', aw));
38178                 this.iframe.style.width = aw + 'px';
38179             }
38180             if(typeof h == 'number'){
38181                 var tbh = 0;
38182                 for (var i =0; i < this.toolbars.length;i++) {
38183                     // fixme - ask toolbars for heights?
38184                     tbh += this.toolbars[i].tb.el.getHeight();
38185                 }
38186                 
38187                 
38188                 
38189                 
38190                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38191                 this.el.setHeight(this.adjustWidth('textarea', ah));
38192                 this.iframe.style.height = ah + 'px';
38193                 if(this.doc){
38194                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38195                 }
38196             }
38197         }
38198     },
38199
38200     /**
38201      * Toggles the editor between standard and source edit mode.
38202      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38203      */
38204     toggleSourceEdit : function(sourceEditMode){
38205         
38206         this.sourceEditMode = sourceEditMode === true;
38207         
38208         if(this.sourceEditMode){
38209           
38210             this.syncValue();
38211             this.iframe.className = 'x-hidden';
38212             this.el.removeClass('x-hidden');
38213             this.el.dom.removeAttribute('tabIndex');
38214             this.el.focus();
38215         }else{
38216              
38217             this.pushValue();
38218             this.iframe.className = '';
38219             this.el.addClass('x-hidden');
38220             this.el.dom.setAttribute('tabIndex', -1);
38221             this.deferFocus();
38222         }
38223         this.setSize(this.wrap.getSize());
38224         this.fireEvent('editmodechange', this, this.sourceEditMode);
38225     },
38226
38227     // private used internally
38228     createLink : function(){
38229         var url = prompt(this.createLinkText, this.defaultLinkValue);
38230         if(url && url != 'http:/'+'/'){
38231             this.relayCmd('createlink', url);
38232         }
38233     },
38234
38235     // private (for BoxComponent)
38236     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38237
38238     // private (for BoxComponent)
38239     getResizeEl : function(){
38240         return this.wrap;
38241     },
38242
38243     // private (for BoxComponent)
38244     getPositionEl : function(){
38245         return this.wrap;
38246     },
38247
38248     // private
38249     initEvents : function(){
38250         this.originalValue = this.getValue();
38251     },
38252
38253     /**
38254      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38255      * @method
38256      */
38257     markInvalid : Roo.emptyFn,
38258     /**
38259      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38260      * @method
38261      */
38262     clearInvalid : Roo.emptyFn,
38263
38264     setValue : function(v){
38265         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38266         this.pushValue();
38267     },
38268
38269     /**
38270      * Protected method that will not generally be called directly. If you need/want
38271      * custom HTML cleanup, this is the method you should override.
38272      * @param {String} html The HTML to be cleaned
38273      * return {String} The cleaned HTML
38274      */
38275     cleanHtml : function(html){
38276         html = String(html);
38277         if(html.length > 5){
38278             if(Roo.isSafari){ // strip safari nonsense
38279                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38280             }
38281         }
38282         if(html == '&nbsp;'){
38283             html = '';
38284         }
38285         return html;
38286     },
38287
38288     /**
38289      * Protected method that will not generally be called directly. Syncs the contents
38290      * of the editor iframe with the textarea.
38291      */
38292     syncValue : function(){
38293         if(this.initialized){
38294             var bd = (this.doc.body || this.doc.documentElement);
38295             var html = bd.innerHTML;
38296             if(Roo.isSafari){
38297                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38298                 var m = bs.match(/text-align:(.*?);/i);
38299                 if(m && m[1]){
38300                     html = '<div style="'+m[0]+'">' + html + '</div>';
38301                 }
38302             }
38303             html = this.cleanHtml(html);
38304             if(this.fireEvent('beforesync', this, html) !== false){
38305                 this.el.dom.value = html;
38306                 this.fireEvent('sync', this, html);
38307             }
38308         }
38309     },
38310
38311     /**
38312      * Protected method that will not generally be called directly. Pushes the value of the textarea
38313      * into the iframe editor.
38314      */
38315     pushValue : function(){
38316         if(this.initialized){
38317             var v = this.el.dom.value;
38318             if(v.length < 1){
38319                 v = '&#160;';
38320             }
38321             if(this.fireEvent('beforepush', this, v) !== false){
38322                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38323                 this.fireEvent('push', this, v);
38324             }
38325         }
38326     },
38327
38328     // private
38329     deferFocus : function(){
38330         this.focus.defer(10, this);
38331     },
38332
38333     // doc'ed in Field
38334     focus : function(){
38335         if(this.win && !this.sourceEditMode){
38336             this.win.focus();
38337         }else{
38338             this.el.focus();
38339         }
38340     },
38341     
38342     assignDocWin: function()
38343     {
38344         var iframe = this.iframe;
38345         
38346          if(Roo.isIE){
38347             this.doc = iframe.contentWindow.document;
38348             this.win = iframe.contentWindow;
38349         } else {
38350             if (!Roo.get(this.frameId)) {
38351                 return;
38352             }
38353             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38354             this.win = Roo.get(this.frameId).dom.contentWindow;
38355         }
38356     },
38357     
38358     // private
38359     initEditor : function(){
38360         //console.log("INIT EDITOR");
38361         this.assignDocWin();
38362         
38363         
38364         
38365         this.doc.designMode="on";
38366         this.doc.open();
38367         this.doc.write(this.getDocMarkup());
38368         this.doc.close();
38369         
38370         var dbody = (this.doc.body || this.doc.documentElement);
38371         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38372         // this copies styles from the containing element into thsi one..
38373         // not sure why we need all of this..
38374         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38375         ss['background-attachment'] = 'fixed'; // w3c
38376         dbody.bgProperties = 'fixed'; // ie
38377         Roo.DomHelper.applyStyles(dbody, ss);
38378         Roo.EventManager.on(this.doc, {
38379             'mousedown': this.onEditorEvent,
38380             'dblclick': this.onEditorEvent,
38381             'click': this.onEditorEvent,
38382             'keyup': this.onEditorEvent,
38383             buffer:100,
38384             scope: this
38385         });
38386         if(Roo.isGecko){
38387             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38388         }
38389         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38390             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38391         }
38392         this.initialized = true;
38393
38394         this.fireEvent('initialize', this);
38395         this.pushValue();
38396     },
38397
38398     // private
38399     onDestroy : function(){
38400         
38401         
38402         
38403         if(this.rendered){
38404             
38405             for (var i =0; i < this.toolbars.length;i++) {
38406                 // fixme - ask toolbars for heights?
38407                 this.toolbars[i].onDestroy();
38408             }
38409             
38410             this.wrap.dom.innerHTML = '';
38411             this.wrap.remove();
38412         }
38413     },
38414
38415     // private
38416     onFirstFocus : function(){
38417         
38418         this.assignDocWin();
38419         
38420         
38421         this.activated = true;
38422         for (var i =0; i < this.toolbars.length;i++) {
38423             this.toolbars[i].onFirstFocus();
38424         }
38425        
38426         if(Roo.isGecko){ // prevent silly gecko errors
38427             this.win.focus();
38428             var s = this.win.getSelection();
38429             if(!s.focusNode || s.focusNode.nodeType != 3){
38430                 var r = s.getRangeAt(0);
38431                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38432                 r.collapse(true);
38433                 this.deferFocus();
38434             }
38435             try{
38436                 this.execCmd('useCSS', true);
38437                 this.execCmd('styleWithCSS', false);
38438             }catch(e){}
38439         }
38440         this.fireEvent('activate', this);
38441     },
38442
38443     // private
38444     adjustFont: function(btn){
38445         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38446         //if(Roo.isSafari){ // safari
38447         //    adjust *= 2;
38448        // }
38449         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38450         if(Roo.isSafari){ // safari
38451             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38452             v =  (v < 10) ? 10 : v;
38453             v =  (v > 48) ? 48 : v;
38454             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38455             
38456         }
38457         
38458         
38459         v = Math.max(1, v+adjust);
38460         
38461         this.execCmd('FontSize', v  );
38462     },
38463
38464     onEditorEvent : function(e){
38465         this.fireEvent('editorevent', this, e);
38466       //  this.updateToolbar();
38467         this.syncValue();
38468     },
38469
38470     insertTag : function(tg)
38471     {
38472         // could be a bit smarter... -> wrap the current selected tRoo..
38473         
38474         this.execCmd("formatblock",   tg);
38475         
38476     },
38477     
38478     insertText : function(txt)
38479     {
38480         
38481         
38482         range = this.createRange();
38483         range.deleteContents();
38484                //alert(Sender.getAttribute('label'));
38485                
38486         range.insertNode(this.doc.createTextNode(txt));
38487     } ,
38488     
38489     // private
38490     relayBtnCmd : function(btn){
38491         this.relayCmd(btn.cmd);
38492     },
38493
38494     /**
38495      * Executes a Midas editor command on the editor document and performs necessary focus and
38496      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38497      * @param {String} cmd The Midas command
38498      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38499      */
38500     relayCmd : function(cmd, value){
38501         this.win.focus();
38502         this.execCmd(cmd, value);
38503         this.fireEvent('editorevent', this);
38504         //this.updateToolbar();
38505         this.deferFocus();
38506     },
38507
38508     /**
38509      * Executes a Midas editor command directly on the editor document.
38510      * For visual commands, you should use {@link #relayCmd} instead.
38511      * <b>This should only be called after the editor is initialized.</b>
38512      * @param {String} cmd The Midas command
38513      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38514      */
38515     execCmd : function(cmd, value){
38516         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38517         this.syncValue();
38518     },
38519
38520     // private
38521     applyCommand : function(e){
38522         if(e.ctrlKey){
38523             var c = e.getCharCode(), cmd;
38524             if(c > 0){
38525                 c = String.fromCharCode(c);
38526                 switch(c){
38527                     case 'b':
38528                         cmd = 'bold';
38529                     break;
38530                     case 'i':
38531                         cmd = 'italic';
38532                     break;
38533                     case 'u':
38534                         cmd = 'underline';
38535                     break;
38536                 }
38537                 if(cmd){
38538                     this.win.focus();
38539                     this.execCmd(cmd);
38540                     this.deferFocus();
38541                     e.preventDefault();
38542                 }
38543             }
38544         }
38545     },
38546
38547     /**
38548      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38549      * to insert tRoo.
38550      * @param {String} text
38551      */
38552     insertAtCursor : function(text){
38553         if(!this.activated){
38554             return;
38555         }
38556         if(Roo.isIE){
38557             this.win.focus();
38558             var r = this.doc.selection.createRange();
38559             if(r){
38560                 r.collapse(true);
38561                 r.pasteHTML(text);
38562                 this.syncValue();
38563                 this.deferFocus();
38564             }
38565         }else if(Roo.isGecko || Roo.isOpera){
38566             this.win.focus();
38567             this.execCmd('InsertHTML', text);
38568             this.deferFocus();
38569         }else if(Roo.isSafari){
38570             this.execCmd('InsertText', text);
38571             this.deferFocus();
38572         }
38573     },
38574
38575     // private
38576     fixKeys : function(){ // load time branching for fastest keydown performance
38577         if(Roo.isIE){
38578             return function(e){
38579                 var k = e.getKey(), r;
38580                 if(k == e.TAB){
38581                     e.stopEvent();
38582                     r = this.doc.selection.createRange();
38583                     if(r){
38584                         r.collapse(true);
38585                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38586                         this.deferFocus();
38587                     }
38588                 }else if(k == e.ENTER){
38589                     r = this.doc.selection.createRange();
38590                     if(r){
38591                         var target = r.parentElement();
38592                         if(!target || target.tagName.toLowerCase() != 'li'){
38593                             e.stopEvent();
38594                             r.pasteHTML('<br />');
38595                             r.collapse(false);
38596                             r.select();
38597                         }
38598                     }
38599                 }
38600             };
38601         }else if(Roo.isOpera){
38602             return function(e){
38603                 var k = e.getKey();
38604                 if(k == e.TAB){
38605                     e.stopEvent();
38606                     this.win.focus();
38607                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38608                     this.deferFocus();
38609                 }
38610             };
38611         }else if(Roo.isSafari){
38612             return function(e){
38613                 var k = e.getKey();
38614                 if(k == e.TAB){
38615                     e.stopEvent();
38616                     this.execCmd('InsertText','\t');
38617                     this.deferFocus();
38618                 }
38619              };
38620         }
38621     }(),
38622     
38623     getAllAncestors: function()
38624     {
38625         var p = this.getSelectedNode();
38626         var a = [];
38627         if (!p) {
38628             a.push(p); // push blank onto stack..
38629             p = this.getParentElement();
38630         }
38631         
38632         
38633         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38634             a.push(p);
38635             p = p.parentNode;
38636         }
38637         a.push(this.doc.body);
38638         return a;
38639     },
38640     lastSel : false,
38641     lastSelNode : false,
38642     
38643     
38644     getSelection : function() 
38645     {
38646         this.assignDocWin();
38647         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38648     },
38649     
38650     getSelectedNode: function() 
38651     {
38652         // this may only work on Gecko!!!
38653         
38654         // should we cache this!!!!
38655         
38656         
38657         
38658          
38659         var range = this.createRange(this.getSelection());
38660         
38661         if (Roo.isIE) {
38662             var parent = range.parentElement();
38663             while (true) {
38664                 var testRange = range.duplicate();
38665                 testRange.moveToElementText(parent);
38666                 if (testRange.inRange(range)) {
38667                     break;
38668                 }
38669                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38670                     break;
38671                 }
38672                 parent = parent.parentElement;
38673             }
38674             return parent;
38675         }
38676         
38677         
38678         var ar = range.endContainer.childNodes;
38679         if (!ar.length) {
38680             ar = range.commonAncestorContainer.childNodes;
38681             //alert(ar.length);
38682         }
38683         var nodes = [];
38684         var other_nodes = [];
38685         var has_other_nodes = false;
38686         for (var i=0;i<ar.length;i++) {
38687             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38688                 continue;
38689             }
38690             // fullly contained node.
38691             
38692             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38693                 nodes.push(ar[i]);
38694                 continue;
38695             }
38696             
38697             // probably selected..
38698             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38699                 other_nodes.push(ar[i]);
38700                 continue;
38701             }
38702             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38703                 continue;
38704             }
38705             
38706             
38707             has_other_nodes = true;
38708         }
38709         if (!nodes.length && other_nodes.length) {
38710             nodes= other_nodes;
38711         }
38712         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38713             return false;
38714         }
38715         
38716         return nodes[0];
38717     },
38718     createRange: function(sel)
38719     {
38720         // this has strange effects when using with 
38721         // top toolbar - not sure if it's a great idea.
38722         //this.editor.contentWindow.focus();
38723         if (typeof sel != "undefined") {
38724             try {
38725                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38726             } catch(e) {
38727                 return this.doc.createRange();
38728             }
38729         } else {
38730             return this.doc.createRange();
38731         }
38732     },
38733     getParentElement: function()
38734     {
38735         
38736         this.assignDocWin();
38737         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38738         
38739         var range = this.createRange(sel);
38740          
38741         try {
38742             var p = range.commonAncestorContainer;
38743             while (p.nodeType == 3) { // text node
38744                 p = p.parentNode;
38745             }
38746             return p;
38747         } catch (e) {
38748             return null;
38749         }
38750     
38751     },
38752     
38753     
38754     
38755     // BC Hacks - cause I cant work out what i was trying to do..
38756     rangeIntersectsNode : function(range, node)
38757     {
38758         var nodeRange = node.ownerDocument.createRange();
38759         try {
38760             nodeRange.selectNode(node);
38761         }
38762         catch (e) {
38763             nodeRange.selectNodeContents(node);
38764         }
38765
38766         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38767                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38768     },
38769     rangeCompareNode : function(range, node) {
38770         var nodeRange = node.ownerDocument.createRange();
38771         try {
38772             nodeRange.selectNode(node);
38773         } catch (e) {
38774             nodeRange.selectNodeContents(node);
38775         }
38776         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38777         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38778
38779         if (nodeIsBefore && !nodeIsAfter)
38780             return 0;
38781         if (!nodeIsBefore && nodeIsAfter)
38782             return 1;
38783         if (nodeIsBefore && nodeIsAfter)
38784             return 2;
38785
38786         return 3;
38787     }
38788
38789     
38790     
38791     // hide stuff that is not compatible
38792     /**
38793      * @event blur
38794      * @hide
38795      */
38796     /**
38797      * @event change
38798      * @hide
38799      */
38800     /**
38801      * @event focus
38802      * @hide
38803      */
38804     /**
38805      * @event specialkey
38806      * @hide
38807      */
38808     /**
38809      * @cfg {String} fieldClass @hide
38810      */
38811     /**
38812      * @cfg {String} focusClass @hide
38813      */
38814     /**
38815      * @cfg {String} autoCreate @hide
38816      */
38817     /**
38818      * @cfg {String} inputType @hide
38819      */
38820     /**
38821      * @cfg {String} invalidClass @hide
38822      */
38823     /**
38824      * @cfg {String} invalidText @hide
38825      */
38826     /**
38827      * @cfg {String} msgFx @hide
38828      */
38829     /**
38830      * @cfg {String} validateOnBlur @hide
38831      */
38832 });// <script type="text/javascript">
38833 /*
38834  * Based on
38835  * Ext JS Library 1.1.1
38836  * Copyright(c) 2006-2007, Ext JS, LLC.
38837  *  
38838  
38839  */
38840
38841 /**
38842  * @class Roo.form.HtmlEditorToolbar1
38843  * Basic Toolbar
38844  * 
38845  * Usage:
38846  *
38847  new Roo.form.HtmlEditor({
38848     ....
38849     toolbars : [
38850         new Roo.form.HtmlEditorToolbar1({
38851             disable : { fonts: 1 , format: 1, ..., ... , ...],
38852             btns : [ .... ]
38853         })
38854     }
38855      
38856  * 
38857  * @cfg {Object} disable List of elements to disable..
38858  * @cfg {Array} btns List of additional buttons.
38859  * 
38860  * 
38861  * NEEDS Extra CSS? 
38862  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38863  */
38864  
38865 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38866 {
38867     
38868     Roo.apply(this, config);
38869     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38870     // dont call parent... till later.
38871 }
38872
38873 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38874     
38875     tb: false,
38876     
38877     rendered: false,
38878     
38879     editor : false,
38880     /**
38881      * @cfg {Object} disable  List of toolbar elements to disable
38882          
38883      */
38884     disable : false,
38885       /**
38886      * @cfg {Array} fontFamilies An array of available font families
38887      */
38888     fontFamilies : [
38889         'Arial',
38890         'Courier New',
38891         'Tahoma',
38892         'Times New Roman',
38893         'Verdana'
38894     ],
38895     
38896     specialChars : [
38897            "&#169;",
38898           "&#174;",     
38899           "&#8482;",    
38900           "&#163;" ,    
38901          // "&#8212;",    
38902           "&#8230;",    
38903           "&#247;" ,    
38904         //  "&#225;" ,     ?? a acute?
38905            "&#8364;"    , //Euro
38906        //   "&#8220;"    ,
38907         //  "&#8221;"    ,
38908         //  "&#8226;"    ,
38909           "&#176;"  //   , // degrees
38910
38911          // "&#233;"     , // e ecute
38912          // "&#250;"     , // u ecute?
38913     ],
38914     inputElements : [ 
38915             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38916             "input:submit", "input:button", "select", "textarea", "label" ],
38917     formats : [
38918         ["p"] ,  
38919         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38920         ["pre"],[ "code"], 
38921         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38922     ],
38923      /**
38924      * @cfg {String} defaultFont default font to use.
38925      */
38926     defaultFont: 'tahoma',
38927    
38928     fontSelect : false,
38929     
38930     
38931     formatCombo : false,
38932     
38933     init : function(editor)
38934     {
38935         this.editor = editor;
38936         
38937         
38938         var fid = editor.frameId;
38939         var etb = this;
38940         function btn(id, toggle, handler){
38941             var xid = fid + '-'+ id ;
38942             return {
38943                 id : xid,
38944                 cmd : id,
38945                 cls : 'x-btn-icon x-edit-'+id,
38946                 enableToggle:toggle !== false,
38947                 scope: editor, // was editor...
38948                 handler:handler||editor.relayBtnCmd,
38949                 clickEvent:'mousedown',
38950                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38951                 tabIndex:-1
38952             };
38953         }
38954         
38955         
38956         
38957         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38958         this.tb = tb;
38959          // stop form submits
38960         tb.el.on('click', function(e){
38961             e.preventDefault(); // what does this do?
38962         });
38963
38964         if(!this.disable.font && !Roo.isSafari){
38965             /* why no safari for fonts
38966             editor.fontSelect = tb.el.createChild({
38967                 tag:'select',
38968                 tabIndex: -1,
38969                 cls:'x-font-select',
38970                 html: editor.createFontOptions()
38971             });
38972             editor.fontSelect.on('change', function(){
38973                 var font = editor.fontSelect.dom.value;
38974                 editor.relayCmd('fontname', font);
38975                 editor.deferFocus();
38976             }, editor);
38977             tb.add(
38978                 editor.fontSelect.dom,
38979                 '-'
38980             );
38981             */
38982         };
38983         if(!this.disable.formats){
38984             this.formatCombo = new Roo.form.ComboBox({
38985                 store: new Roo.data.SimpleStore({
38986                     id : 'tag',
38987                     fields: ['tag'],
38988                     data : this.formats // from states.js
38989                 }),
38990                 blockFocus : true,
38991                 //autoCreate : {tag: "div",  size: "20"},
38992                 displayField:'tag',
38993                 typeAhead: false,
38994                 mode: 'local',
38995                 editable : false,
38996                 triggerAction: 'all',
38997                 emptyText:'Add tag',
38998                 selectOnFocus:true,
38999                 width:135,
39000                 listeners : {
39001                     'select': function(c, r, i) {
39002                         editor.insertTag(r.get('tag'));
39003                         editor.focus();
39004                     }
39005                 }
39006
39007             });
39008             tb.addField(this.formatCombo);
39009             
39010         }
39011         
39012         if(!this.disable.format){
39013             tb.add(
39014                 btn('bold'),
39015                 btn('italic'),
39016                 btn('underline')
39017             );
39018         };
39019         if(!this.disable.fontSize){
39020             tb.add(
39021                 '-',
39022                 
39023                 
39024                 btn('increasefontsize', false, editor.adjustFont),
39025                 btn('decreasefontsize', false, editor.adjustFont)
39026             );
39027         };
39028         
39029         
39030         if(this.disable.colors){
39031             tb.add(
39032                 '-', {
39033                     id:editor.frameId +'-forecolor',
39034                     cls:'x-btn-icon x-edit-forecolor',
39035                     clickEvent:'mousedown',
39036                     tooltip: this.buttonTips['forecolor'] || undefined,
39037                     tabIndex:-1,
39038                     menu : new Roo.menu.ColorMenu({
39039                         allowReselect: true,
39040                         focus: Roo.emptyFn,
39041                         value:'000000',
39042                         plain:true,
39043                         selectHandler: function(cp, color){
39044                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39045                             editor.deferFocus();
39046                         },
39047                         scope: editor,
39048                         clickEvent:'mousedown'
39049                     })
39050                 }, {
39051                     id:editor.frameId +'backcolor',
39052                     cls:'x-btn-icon x-edit-backcolor',
39053                     clickEvent:'mousedown',
39054                     tooltip: this.buttonTips['backcolor'] || undefined,
39055                     tabIndex:-1,
39056                     menu : new Roo.menu.ColorMenu({
39057                         focus: Roo.emptyFn,
39058                         value:'FFFFFF',
39059                         plain:true,
39060                         allowReselect: true,
39061                         selectHandler: function(cp, color){
39062                             if(Roo.isGecko){
39063                                 editor.execCmd('useCSS', false);
39064                                 editor.execCmd('hilitecolor', color);
39065                                 editor.execCmd('useCSS', true);
39066                                 editor.deferFocus();
39067                             }else{
39068                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39069                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39070                                 editor.deferFocus();
39071                             }
39072                         },
39073                         scope:editor,
39074                         clickEvent:'mousedown'
39075                     })
39076                 }
39077             );
39078         };
39079         // now add all the items...
39080         
39081
39082         if(!this.disable.alignments){
39083             tb.add(
39084                 '-',
39085                 btn('justifyleft'),
39086                 btn('justifycenter'),
39087                 btn('justifyright')
39088             );
39089         };
39090
39091         //if(!Roo.isSafari){
39092             if(!this.disable.links){
39093                 tb.add(
39094                     '-',
39095                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39096                 );
39097             };
39098
39099             if(!this.disable.lists){
39100                 tb.add(
39101                     '-',
39102                     btn('insertorderedlist'),
39103                     btn('insertunorderedlist')
39104                 );
39105             }
39106             if(!this.disable.sourceEdit){
39107                 tb.add(
39108                     '-',
39109                     btn('sourceedit', true, function(btn){
39110                         this.toggleSourceEdit(btn.pressed);
39111                     })
39112                 );
39113             }
39114         //}
39115         
39116         var smenu = { };
39117         // special menu.. - needs to be tidied up..
39118         if (!this.disable.special) {
39119             smenu = {
39120                 text: "&#169;",
39121                 cls: 'x-edit-none',
39122                 menu : {
39123                     items : []
39124                    }
39125             };
39126             for (var i =0; i < this.specialChars.length; i++) {
39127                 smenu.menu.items.push({
39128                     
39129                     text: this.specialChars[i],
39130                     handler: function(a,b) {
39131                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
39132                     },
39133                     tabIndex:-1
39134                 });
39135             }
39136             
39137             
39138             tb.add(smenu);
39139             
39140             
39141         }
39142         if (this.btns) {
39143             for(var i =0; i< this.btns.length;i++) {
39144                 var b = this.btns[i];
39145                 b.cls =  'x-edit-none';
39146                 b.scope = editor;
39147                 tb.add(b);
39148             }
39149         
39150         }
39151         
39152         
39153         
39154         // disable everything...
39155         
39156         this.tb.items.each(function(item){
39157            if(item.id != editor.frameId+ '-sourceedit'){
39158                 item.disable();
39159             }
39160         });
39161         this.rendered = true;
39162         
39163         // the all the btns;
39164         editor.on('editorevent', this.updateToolbar, this);
39165         // other toolbars need to implement this..
39166         //editor.on('editmodechange', this.updateToolbar, this);
39167     },
39168     
39169     
39170     
39171     /**
39172      * Protected method that will not generally be called directly. It triggers
39173      * a toolbar update by reading the markup state of the current selection in the editor.
39174      */
39175     updateToolbar: function(){
39176
39177         if(!this.editor.activated){
39178             this.editor.onFirstFocus();
39179             return;
39180         }
39181
39182         var btns = this.tb.items.map, 
39183             doc = this.editor.doc,
39184             frameId = this.editor.frameId;
39185
39186         if(!this.disable.font && !Roo.isSafari){
39187             /*
39188             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39189             if(name != this.fontSelect.dom.value){
39190                 this.fontSelect.dom.value = name;
39191             }
39192             */
39193         }
39194         if(!this.disable.format){
39195             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39196             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39197             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39198         }
39199         if(!this.disable.alignments){
39200             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39201             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39202             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39203         }
39204         if(!Roo.isSafari && !this.disable.lists){
39205             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39206             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39207         }
39208         
39209         var ans = this.editor.getAllAncestors();
39210         if (this.formatCombo) {
39211             
39212             
39213             var store = this.formatCombo.store;
39214             this.formatCombo.setValue("");
39215             for (var i =0; i < ans.length;i++) {
39216                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39217                     // select it..
39218                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39219                     break;
39220                 }
39221             }
39222         }
39223         
39224         
39225         
39226         // hides menus... - so this cant be on a menu...
39227         Roo.menu.MenuMgr.hideAll();
39228
39229         //this.editorsyncValue();
39230     },
39231    
39232     
39233     createFontOptions : function(){
39234         var buf = [], fs = this.fontFamilies, ff, lc;
39235         for(var i = 0, len = fs.length; i< len; i++){
39236             ff = fs[i];
39237             lc = ff.toLowerCase();
39238             buf.push(
39239                 '<option value="',lc,'" style="font-family:',ff,';"',
39240                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39241                     ff,
39242                 '</option>'
39243             );
39244         }
39245         return buf.join('');
39246     },
39247     
39248     toggleSourceEdit : function(sourceEditMode){
39249         if(sourceEditMode === undefined){
39250             sourceEditMode = !this.sourceEditMode;
39251         }
39252         this.sourceEditMode = sourceEditMode === true;
39253         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39254         // just toggle the button?
39255         if(btn.pressed !== this.editor.sourceEditMode){
39256             btn.toggle(this.editor.sourceEditMode);
39257             return;
39258         }
39259         
39260         if(this.sourceEditMode){
39261             this.tb.items.each(function(item){
39262                 if(item.cmd != 'sourceedit'){
39263                     item.disable();
39264                 }
39265             });
39266           
39267         }else{
39268             if(this.initialized){
39269                 this.tb.items.each(function(item){
39270                     item.enable();
39271                 });
39272             }
39273             
39274         }
39275         // tell the editor that it's been pressed..
39276         this.editor.toggleSourceEdit(sourceEditMode);
39277        
39278     },
39279      /**
39280      * Object collection of toolbar tooltips for the buttons in the editor. The key
39281      * is the command id associated with that button and the value is a valid QuickTips object.
39282      * For example:
39283 <pre><code>
39284 {
39285     bold : {
39286         title: 'Bold (Ctrl+B)',
39287         text: 'Make the selected text bold.',
39288         cls: 'x-html-editor-tip'
39289     },
39290     italic : {
39291         title: 'Italic (Ctrl+I)',
39292         text: 'Make the selected text italic.',
39293         cls: 'x-html-editor-tip'
39294     },
39295     ...
39296 </code></pre>
39297     * @type Object
39298      */
39299     buttonTips : {
39300         bold : {
39301             title: 'Bold (Ctrl+B)',
39302             text: 'Make the selected text bold.',
39303             cls: 'x-html-editor-tip'
39304         },
39305         italic : {
39306             title: 'Italic (Ctrl+I)',
39307             text: 'Make the selected text italic.',
39308             cls: 'x-html-editor-tip'
39309         },
39310         underline : {
39311             title: 'Underline (Ctrl+U)',
39312             text: 'Underline the selected text.',
39313             cls: 'x-html-editor-tip'
39314         },
39315         increasefontsize : {
39316             title: 'Grow Text',
39317             text: 'Increase the font size.',
39318             cls: 'x-html-editor-tip'
39319         },
39320         decreasefontsize : {
39321             title: 'Shrink Text',
39322             text: 'Decrease the font size.',
39323             cls: 'x-html-editor-tip'
39324         },
39325         backcolor : {
39326             title: 'Text Highlight Color',
39327             text: 'Change the background color of the selected text.',
39328             cls: 'x-html-editor-tip'
39329         },
39330         forecolor : {
39331             title: 'Font Color',
39332             text: 'Change the color of the selected text.',
39333             cls: 'x-html-editor-tip'
39334         },
39335         justifyleft : {
39336             title: 'Align Text Left',
39337             text: 'Align text to the left.',
39338             cls: 'x-html-editor-tip'
39339         },
39340         justifycenter : {
39341             title: 'Center Text',
39342             text: 'Center text in the editor.',
39343             cls: 'x-html-editor-tip'
39344         },
39345         justifyright : {
39346             title: 'Align Text Right',
39347             text: 'Align text to the right.',
39348             cls: 'x-html-editor-tip'
39349         },
39350         insertunorderedlist : {
39351             title: 'Bullet List',
39352             text: 'Start a bulleted list.',
39353             cls: 'x-html-editor-tip'
39354         },
39355         insertorderedlist : {
39356             title: 'Numbered List',
39357             text: 'Start a numbered list.',
39358             cls: 'x-html-editor-tip'
39359         },
39360         createlink : {
39361             title: 'Hyperlink',
39362             text: 'Make the selected text a hyperlink.',
39363             cls: 'x-html-editor-tip'
39364         },
39365         sourceedit : {
39366             title: 'Source Edit',
39367             text: 'Switch to source editing mode.',
39368             cls: 'x-html-editor-tip'
39369         }
39370     },
39371     // private
39372     onDestroy : function(){
39373         if(this.rendered){
39374             
39375             this.tb.items.each(function(item){
39376                 if(item.menu){
39377                     item.menu.removeAll();
39378                     if(item.menu.el){
39379                         item.menu.el.destroy();
39380                     }
39381                 }
39382                 item.destroy();
39383             });
39384              
39385         }
39386     },
39387     onFirstFocus: function() {
39388         this.tb.items.each(function(item){
39389            item.enable();
39390         });
39391     }
39392 });
39393
39394
39395
39396
39397 // <script type="text/javascript">
39398 /*
39399  * Based on
39400  * Ext JS Library 1.1.1
39401  * Copyright(c) 2006-2007, Ext JS, LLC.
39402  *  
39403  
39404  */
39405
39406  
39407 /**
39408  * @class Roo.form.HtmlEditor.ToolbarContext
39409  * Context Toolbar
39410  * 
39411  * Usage:
39412  *
39413  new Roo.form.HtmlEditor({
39414     ....
39415     toolbars : [
39416         new Roo.form.HtmlEditor.ToolbarStandard(),
39417         new Roo.form.HtmlEditor.ToolbarContext()
39418         })
39419     }
39420      
39421  * 
39422  * @config : {Object} disable List of elements to disable.. (not done yet.)
39423  * 
39424  * 
39425  */
39426
39427 Roo.form.HtmlEditor.ToolbarContext = function(config)
39428 {
39429     
39430     Roo.apply(this, config);
39431     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39432     // dont call parent... till later.
39433 }
39434 Roo.form.HtmlEditor.ToolbarContext.types = {
39435     'IMG' : {
39436         width : {
39437             title: "Width",
39438             width: 40
39439         },
39440         height:  {
39441             title: "Height",
39442             width: 40
39443         },
39444         align: {
39445             title: "Align",
39446             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39447             width : 80
39448             
39449         },
39450         border: {
39451             title: "Border",
39452             width: 40
39453         },
39454         alt: {
39455             title: "Alt",
39456             width: 120
39457         },
39458         src : {
39459             title: "Src",
39460             width: 220
39461         }
39462         
39463     },
39464     'A' : {
39465         name : {
39466             title: "Name",
39467             width: 50
39468         },
39469         href:  {
39470             title: "Href",
39471             width: 220
39472         } // border?
39473         
39474     },
39475     'TABLE' : {
39476         rows : {
39477             title: "Rows",
39478             width: 20
39479         },
39480         cols : {
39481             title: "Cols",
39482             width: 20
39483         },
39484         width : {
39485             title: "Width",
39486             width: 40
39487         },
39488         height : {
39489             title: "Height",
39490             width: 40
39491         },
39492         border : {
39493             title: "Border",
39494             width: 20
39495         }
39496     },
39497     'TD' : {
39498         width : {
39499             title: "Width",
39500             width: 40
39501         },
39502         height : {
39503             title: "Height",
39504             width: 40
39505         },   
39506         align: {
39507             title: "Align",
39508             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39509             width: 40
39510         },
39511         valign: {
39512             title: "Valign",
39513             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39514             width: 40
39515         },
39516         colspan: {
39517             title: "Colspan",
39518             width: 20
39519             
39520         }
39521     },
39522     'INPUT' : {
39523         name : {
39524             title: "name",
39525             width: 120
39526         },
39527         value : {
39528             title: "Value",
39529             width: 120
39530         },
39531         width : {
39532             title: "Width",
39533             width: 40
39534         }
39535     },
39536     'LABEL' : {
39537         'for' : {
39538             title: "For",
39539             width: 120
39540         }
39541     },
39542     'TEXTAREA' : {
39543           name : {
39544             title: "name",
39545             width: 120
39546         },
39547         rows : {
39548             title: "Rows",
39549             width: 20
39550         },
39551         cols : {
39552             title: "Cols",
39553             width: 20
39554         }
39555     },
39556     'SELECT' : {
39557         name : {
39558             title: "name",
39559             width: 120
39560         },
39561         selectoptions : {
39562             title: "Options",
39563             width: 200
39564         }
39565     },
39566     'BODY' : {
39567         title : {
39568             title: "title",
39569             width: 120,
39570             disabled : true
39571         }
39572     }
39573 };
39574
39575
39576
39577 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39578     
39579     tb: false,
39580     
39581     rendered: false,
39582     
39583     editor : false,
39584     /**
39585      * @cfg {Object} disable  List of toolbar elements to disable
39586          
39587      */
39588     disable : false,
39589     
39590     
39591     
39592     toolbars : false,
39593     
39594     init : function(editor)
39595     {
39596         this.editor = editor;
39597         
39598         
39599         var fid = editor.frameId;
39600         var etb = this;
39601         function btn(id, toggle, handler){
39602             var xid = fid + '-'+ id ;
39603             return {
39604                 id : xid,
39605                 cmd : id,
39606                 cls : 'x-btn-icon x-edit-'+id,
39607                 enableToggle:toggle !== false,
39608                 scope: editor, // was editor...
39609                 handler:handler||editor.relayBtnCmd,
39610                 clickEvent:'mousedown',
39611                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39612                 tabIndex:-1
39613             };
39614         }
39615         // create a new element.
39616         var wdiv = editor.wrap.createChild({
39617                 tag: 'div'
39618             }, editor.wrap.dom.firstChild.nextSibling, true);
39619         
39620         // can we do this more than once??
39621         
39622          // stop form submits
39623       
39624  
39625         // disable everything...
39626         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39627         this.toolbars = {};
39628            
39629         for (var i in  ty) {
39630             this.toolbars[i] = this.buildToolbar(ty[i],i);
39631         }
39632         this.tb = this.toolbars.BODY;
39633         this.tb.el.show();
39634         
39635          
39636         this.rendered = true;
39637         
39638         // the all the btns;
39639         editor.on('editorevent', this.updateToolbar, this);
39640         // other toolbars need to implement this..
39641         //editor.on('editmodechange', this.updateToolbar, this);
39642     },
39643     
39644     
39645     
39646     /**
39647      * Protected method that will not generally be called directly. It triggers
39648      * a toolbar update by reading the markup state of the current selection in the editor.
39649      */
39650     updateToolbar: function(){
39651
39652         if(!this.editor.activated){
39653             this.editor.onFirstFocus();
39654             return;
39655         }
39656
39657         
39658         var ans = this.editor.getAllAncestors();
39659         
39660         // pick
39661         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39662         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39663         sel = sel ? sel : this.editor.doc.body;
39664         sel = sel.tagName.length ? sel : this.editor.doc.body;
39665         var tn = sel.tagName.toUpperCase();
39666         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39667         tn = sel.tagName.toUpperCase();
39668         if (this.tb.name  == tn) {
39669             return; // no change
39670         }
39671         this.tb.el.hide();
39672         ///console.log("show: " + tn);
39673         this.tb =  this.toolbars[tn];
39674         this.tb.el.show();
39675         this.tb.fields.each(function(e) {
39676             e.setValue(sel.getAttribute(e.name));
39677         });
39678         this.tb.selectedNode = sel;
39679         
39680         
39681         Roo.menu.MenuMgr.hideAll();
39682
39683         //this.editorsyncValue();
39684     },
39685    
39686        
39687     // private
39688     onDestroy : function(){
39689         if(this.rendered){
39690             
39691             this.tb.items.each(function(item){
39692                 if(item.menu){
39693                     item.menu.removeAll();
39694                     if(item.menu.el){
39695                         item.menu.el.destroy();
39696                     }
39697                 }
39698                 item.destroy();
39699             });
39700              
39701         }
39702     },
39703     onFirstFocus: function() {
39704         // need to do this for all the toolbars..
39705         this.tb.items.each(function(item){
39706            item.enable();
39707         });
39708     },
39709     buildToolbar: function(tlist, nm)
39710     {
39711         var editor = this.editor;
39712          // create a new element.
39713         var wdiv = editor.wrap.createChild({
39714                 tag: 'div'
39715             }, editor.wrap.dom.firstChild.nextSibling, true);
39716         
39717        
39718         var tb = new Roo.Toolbar(wdiv);
39719         tb.add(nm+ ":&nbsp;");
39720         for (var i in tlist) {
39721             var item = tlist[i];
39722             tb.add(item.title + ":&nbsp;");
39723             if (item.opts) {
39724                 // fixme
39725                 
39726               
39727                 tb.addField( new Roo.form.ComboBox({
39728                     store: new Roo.data.SimpleStore({
39729                         id : 'val',
39730                         fields: ['val'],
39731                         data : item.opts // from states.js
39732                     }),
39733                     name : i,
39734                     displayField:'val',
39735                     typeAhead: false,
39736                     mode: 'local',
39737                     editable : false,
39738                     triggerAction: 'all',
39739                     emptyText:'Select',
39740                     selectOnFocus:true,
39741                     width: item.width ? item.width  : 130,
39742                     listeners : {
39743                         'select': function(c, r, i) {
39744                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39745                         }
39746                     }
39747
39748                 }));
39749                 continue;
39750                     
39751                 
39752                 
39753                 
39754                 
39755                 tb.addField( new Roo.form.TextField({
39756                     name: i,
39757                     width: 100,
39758                     //allowBlank:false,
39759                     value: ''
39760                 }));
39761                 continue;
39762             }
39763             tb.addField( new Roo.form.TextField({
39764                 name: i,
39765                 width: item.width,
39766                 //allowBlank:true,
39767                 value: '',
39768                 listeners: {
39769                     'change' : function(f, nv, ov) {
39770                         tb.selectedNode.setAttribute(f.name, nv);
39771                     }
39772                 }
39773             }));
39774              
39775         }
39776         tb.el.on('click', function(e){
39777             e.preventDefault(); // what does this do?
39778         });
39779         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39780         tb.el.hide();
39781         tb.name = nm;
39782         // dont need to disable them... as they will get hidden
39783         return tb;
39784          
39785         
39786     }
39787     
39788     
39789     
39790     
39791 });
39792
39793
39794
39795
39796
39797 /*
39798  * Based on:
39799  * Ext JS Library 1.1.1
39800  * Copyright(c) 2006-2007, Ext JS, LLC.
39801  *
39802  * Originally Released Under LGPL - original licence link has changed is not relivant.
39803  *
39804  * Fork - LGPL
39805  * <script type="text/javascript">
39806  */
39807  
39808 /**
39809  * @class Roo.form.BasicForm
39810  * @extends Roo.util.Observable
39811  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39812  * @constructor
39813  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39814  * @param {Object} config Configuration options
39815  */
39816 Roo.form.BasicForm = function(el, config){
39817     this.allItems = [];
39818     this.childForms = [];
39819     Roo.apply(this, config);
39820     /*
39821      * The Roo.form.Field items in this form.
39822      * @type MixedCollection
39823      */
39824      
39825      
39826     this.items = new Roo.util.MixedCollection(false, function(o){
39827         return o.id || (o.id = Roo.id());
39828     });
39829     this.addEvents({
39830         /**
39831          * @event beforeaction
39832          * Fires before any action is performed. Return false to cancel the action.
39833          * @param {Form} this
39834          * @param {Action} action The action to be performed
39835          */
39836         beforeaction: true,
39837         /**
39838          * @event actionfailed
39839          * Fires when an action fails.
39840          * @param {Form} this
39841          * @param {Action} action The action that failed
39842          */
39843         actionfailed : true,
39844         /**
39845          * @event actioncomplete
39846          * Fires when an action is completed.
39847          * @param {Form} this
39848          * @param {Action} action The action that completed
39849          */
39850         actioncomplete : true
39851     });
39852     if(el){
39853         this.initEl(el);
39854     }
39855     Roo.form.BasicForm.superclass.constructor.call(this);
39856 };
39857
39858 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39859     /**
39860      * @cfg {String} method
39861      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39862      */
39863     /**
39864      * @cfg {DataReader} reader
39865      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39866      * This is optional as there is built-in support for processing JSON.
39867      */
39868     /**
39869      * @cfg {DataReader} errorReader
39870      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39871      * This is completely optional as there is built-in support for processing JSON.
39872      */
39873     /**
39874      * @cfg {String} url
39875      * The URL to use for form actions if one isn't supplied in the action options.
39876      */
39877     /**
39878      * @cfg {Boolean} fileUpload
39879      * Set to true if this form is a file upload.
39880      */
39881     /**
39882      * @cfg {Object} baseParams
39883      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39884      */
39885     /**
39886      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39887      */
39888     timeout: 30,
39889
39890     // private
39891     activeAction : null,
39892
39893     /**
39894      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39895      * or setValues() data instead of when the form was first created.
39896      */
39897     trackResetOnLoad : false,
39898     
39899     
39900     /**
39901      * childForms - used for multi-tab forms
39902      * @type {Array}
39903      */
39904     childForms : false,
39905     
39906     /**
39907      * allItems - full list of fields.
39908      * @type {Array}
39909      */
39910     allItems : false,
39911     
39912     /**
39913      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39914      * element by passing it or its id or mask the form itself by passing in true.
39915      * @type Mixed
39916      */
39917     waitMsgTarget : undefined,
39918
39919     // private
39920     initEl : function(el){
39921         this.el = Roo.get(el);
39922         this.id = this.el.id || Roo.id();
39923         this.el.on('submit', this.onSubmit, this);
39924         this.el.addClass('x-form');
39925     },
39926
39927     // private
39928     onSubmit : function(e){
39929         e.stopEvent();
39930     },
39931
39932     /**
39933      * Returns true if client-side validation on the form is successful.
39934      * @return Boolean
39935      */
39936     isValid : function(){
39937         var valid = true;
39938         this.items.each(function(f){
39939            if(!f.validate()){
39940                valid = false;
39941            }
39942         });
39943         return valid;
39944     },
39945
39946     /**
39947      * Returns true if any fields in this form have changed since their original load.
39948      * @return Boolean
39949      */
39950     isDirty : function(){
39951         var dirty = false;
39952         this.items.each(function(f){
39953            if(f.isDirty()){
39954                dirty = true;
39955                return false;
39956            }
39957         });
39958         return dirty;
39959     },
39960
39961     /**
39962      * Performs a predefined action (submit or load) or custom actions you define on this form.
39963      * @param {String} actionName The name of the action type
39964      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39965      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39966      * accept other config options):
39967      * <pre>
39968 Property          Type             Description
39969 ----------------  ---------------  ----------------------------------------------------------------------------------
39970 url               String           The url for the action (defaults to the form's url)
39971 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39972 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39973 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39974                                    validate the form on the client (defaults to false)
39975      * </pre>
39976      * @return {BasicForm} this
39977      */
39978     doAction : function(action, options){
39979         if(typeof action == 'string'){
39980             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39981         }
39982         if(this.fireEvent('beforeaction', this, action) !== false){
39983             this.beforeAction(action);
39984             action.run.defer(100, action);
39985         }
39986         return this;
39987     },
39988
39989     /**
39990      * Shortcut to do a submit action.
39991      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39992      * @return {BasicForm} this
39993      */
39994     submit : function(options){
39995         this.doAction('submit', options);
39996         return this;
39997     },
39998
39999     /**
40000      * Shortcut to do a load action.
40001      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40002      * @return {BasicForm} this
40003      */
40004     load : function(options){
40005         this.doAction('load', options);
40006         return this;
40007     },
40008
40009     /**
40010      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40011      * @param {Record} record The record to edit
40012      * @return {BasicForm} this
40013      */
40014     updateRecord : function(record){
40015         record.beginEdit();
40016         var fs = record.fields;
40017         fs.each(function(f){
40018             var field = this.findField(f.name);
40019             if(field){
40020                 record.set(f.name, field.getValue());
40021             }
40022         }, this);
40023         record.endEdit();
40024         return this;
40025     },
40026
40027     /**
40028      * Loads an Roo.data.Record into this form.
40029      * @param {Record} record The record to load
40030      * @return {BasicForm} this
40031      */
40032     loadRecord : function(record){
40033         this.setValues(record.data);
40034         return this;
40035     },
40036
40037     // private
40038     beforeAction : function(action){
40039         var o = action.options;
40040         if(o.waitMsg){
40041             if(this.waitMsgTarget === true){
40042                 this.el.mask(o.waitMsg, 'x-mask-loading');
40043             }else if(this.waitMsgTarget){
40044                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40045                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40046             }else{
40047                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40048             }
40049         }
40050     },
40051
40052     // private
40053     afterAction : function(action, success){
40054         this.activeAction = null;
40055         var o = action.options;
40056         if(o.waitMsg){
40057             if(this.waitMsgTarget === true){
40058                 this.el.unmask();
40059             }else if(this.waitMsgTarget){
40060                 this.waitMsgTarget.unmask();
40061             }else{
40062                 Roo.MessageBox.updateProgress(1);
40063                 Roo.MessageBox.hide();
40064             }
40065         }
40066         if(success){
40067             if(o.reset){
40068                 this.reset();
40069             }
40070             Roo.callback(o.success, o.scope, [this, action]);
40071             this.fireEvent('actioncomplete', this, action);
40072         }else{
40073             Roo.callback(o.failure, o.scope, [this, action]);
40074             this.fireEvent('actionfailed', this, action);
40075         }
40076     },
40077
40078     /**
40079      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40080      * @param {String} id The value to search for
40081      * @return Field
40082      */
40083     findField : function(id){
40084         var field = this.items.get(id);
40085         if(!field){
40086             this.items.each(function(f){
40087                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40088                     field = f;
40089                     return false;
40090                 }
40091             });
40092         }
40093         return field || null;
40094     },
40095
40096     /**
40097      * Add a secondary form to this one, 
40098      * Used to provide tabbed forms. One form is primary, with hidden values 
40099      * which mirror the elements from the other forms.
40100      * 
40101      * @param {Roo.form.Form} form to add.
40102      * 
40103      */
40104     addForm : function(form)
40105     {
40106        
40107         if (this.childForms.indexOf(form) > -1) {
40108             // already added..
40109             return;
40110         }
40111         this.childForms.push(form);
40112         var n = '';
40113         Roo.each(form.allItems, function (fe) {
40114             
40115             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40116             if (this.findField(n)) { // already added..
40117                 return;
40118             }
40119             var add = new Roo.form.Hidden({
40120                 name : n
40121             });
40122             add.render(this.el);
40123             
40124             this.add( add );
40125         }, this);
40126         
40127     },
40128     /**
40129      * Mark fields in this form invalid in bulk.
40130      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40131      * @return {BasicForm} this
40132      */
40133     markInvalid : function(errors){
40134         if(errors instanceof Array){
40135             for(var i = 0, len = errors.length; i < len; i++){
40136                 var fieldError = errors[i];
40137                 var f = this.findField(fieldError.id);
40138                 if(f){
40139                     f.markInvalid(fieldError.msg);
40140                 }
40141             }
40142         }else{
40143             var field, id;
40144             for(id in errors){
40145                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40146                     field.markInvalid(errors[id]);
40147                 }
40148             }
40149         }
40150         Roo.each(this.childForms || [], function (f) {
40151             f.markInvalid(errors);
40152         });
40153         
40154         return this;
40155     },
40156
40157     /**
40158      * Set values for fields in this form in bulk.
40159      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40160      * @return {BasicForm} this
40161      */
40162     setValues : function(values){
40163         if(values instanceof Array){ // array of objects
40164             for(var i = 0, len = values.length; i < len; i++){
40165                 var v = values[i];
40166                 var f = this.findField(v.id);
40167                 if(f){
40168                     f.setValue(v.value);
40169                     if(this.trackResetOnLoad){
40170                         f.originalValue = f.getValue();
40171                     }
40172                 }
40173             }
40174         }else{ // object hash
40175             var field, id;
40176             for(id in values){
40177                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40178                     
40179                     if (field.setFromData && 
40180                         field.valueField && 
40181                         field.displayField &&
40182                         // combos' with local stores can 
40183                         // be queried via setValue()
40184                         // to set their value..
40185                         (field.store && !field.store.isLocal)
40186                         ) {
40187                         // it's a combo
40188                         var sd = { };
40189                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40190                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40191                         field.setFromData(sd);
40192                         
40193                     } else {
40194                         field.setValue(values[id]);
40195                     }
40196                     
40197                     
40198                     if(this.trackResetOnLoad){
40199                         field.originalValue = field.getValue();
40200                     }
40201                 }
40202             }
40203         }
40204          
40205         Roo.each(this.childForms || [], function (f) {
40206             f.setValues(values);
40207         });
40208                 
40209         return this;
40210     },
40211
40212     /**
40213      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40214      * they are returned as an array.
40215      * @param {Boolean} asString
40216      * @return {Object}
40217      */
40218     getValues : function(asString){
40219         if (this.childForms) {
40220             // copy values from the child forms
40221             Roo.each(this.childForms, function (f) {
40222                 this.setValues(f.getValues());
40223             }, this);
40224         }
40225         
40226         
40227         
40228         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40229         if(asString === true){
40230             return fs;
40231         }
40232         return Roo.urlDecode(fs);
40233     },
40234
40235     /**
40236      * Clears all invalid messages in this form.
40237      * @return {BasicForm} this
40238      */
40239     clearInvalid : function(){
40240         this.items.each(function(f){
40241            f.clearInvalid();
40242         });
40243         
40244         Roo.each(this.childForms || [], function (f) {
40245             f.clearInvalid();
40246         });
40247         
40248         
40249         return this;
40250     },
40251
40252     /**
40253      * Resets this form.
40254      * @return {BasicForm} this
40255      */
40256     reset : function(){
40257         this.items.each(function(f){
40258             f.reset();
40259         });
40260         
40261         Roo.each(this.childForms || [], function (f) {
40262             f.reset();
40263         });
40264        
40265         
40266         return this;
40267     },
40268
40269     /**
40270      * Add Roo.form components to this form.
40271      * @param {Field} field1
40272      * @param {Field} field2 (optional)
40273      * @param {Field} etc (optional)
40274      * @return {BasicForm} this
40275      */
40276     add : function(){
40277         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40278         return this;
40279     },
40280
40281
40282     /**
40283      * Removes a field from the items collection (does NOT remove its markup).
40284      * @param {Field} field
40285      * @return {BasicForm} this
40286      */
40287     remove : function(field){
40288         this.items.remove(field);
40289         return this;
40290     },
40291
40292     /**
40293      * Looks at the fields in this form, checks them for an id attribute,
40294      * and calls applyTo on the existing dom element with that id.
40295      * @return {BasicForm} this
40296      */
40297     render : function(){
40298         this.items.each(function(f){
40299             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40300                 f.applyTo(f.id);
40301             }
40302         });
40303         return this;
40304     },
40305
40306     /**
40307      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40308      * @param {Object} values
40309      * @return {BasicForm} this
40310      */
40311     applyToFields : function(o){
40312         this.items.each(function(f){
40313            Roo.apply(f, o);
40314         });
40315         return this;
40316     },
40317
40318     /**
40319      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40320      * @param {Object} values
40321      * @return {BasicForm} this
40322      */
40323     applyIfToFields : function(o){
40324         this.items.each(function(f){
40325            Roo.applyIf(f, o);
40326         });
40327         return this;
40328     }
40329 });
40330
40331 // back compat
40332 Roo.BasicForm = Roo.form.BasicForm;/*
40333  * Based on:
40334  * Ext JS Library 1.1.1
40335  * Copyright(c) 2006-2007, Ext JS, LLC.
40336  *
40337  * Originally Released Under LGPL - original licence link has changed is not relivant.
40338  *
40339  * Fork - LGPL
40340  * <script type="text/javascript">
40341  */
40342
40343 /**
40344  * @class Roo.form.Form
40345  * @extends Roo.form.BasicForm
40346  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40347  * @constructor
40348  * @param {Object} config Configuration options
40349  */
40350 Roo.form.Form = function(config){
40351     var xitems =  [];
40352     if (config.items) {
40353         xitems = config.items;
40354         delete config.items;
40355     }
40356    
40357     
40358     Roo.form.Form.superclass.constructor.call(this, null, config);
40359     this.url = this.url || this.action;
40360     if(!this.root){
40361         this.root = new Roo.form.Layout(Roo.applyIf({
40362             id: Roo.id()
40363         }, config));
40364     }
40365     this.active = this.root;
40366     /**
40367      * Array of all the buttons that have been added to this form via {@link addButton}
40368      * @type Array
40369      */
40370     this.buttons = [];
40371     this.allItems = [];
40372     this.addEvents({
40373         /**
40374          * @event clientvalidation
40375          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40376          * @param {Form} this
40377          * @param {Boolean} valid true if the form has passed client-side validation
40378          */
40379         clientvalidation: true,
40380         /**
40381          * @event rendered
40382          * Fires when the form is rendered
40383          * @param {Roo.form.Form} form
40384          */
40385         rendered : true
40386     });
40387     
40388     Roo.each(xitems, this.addxtype, this);
40389     
40390     
40391     
40392 };
40393
40394 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40395     /**
40396      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40397      */
40398     /**
40399      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40400      */
40401     /**
40402      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40403      */
40404     buttonAlign:'center',
40405
40406     /**
40407      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40408      */
40409     minButtonWidth:75,
40410
40411     /**
40412      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40413      * This property cascades to child containers if not set.
40414      */
40415     labelAlign:'left',
40416
40417     /**
40418      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40419      * fires a looping event with that state. This is required to bind buttons to the valid
40420      * state using the config value formBind:true on the button.
40421      */
40422     monitorValid : false,
40423
40424     /**
40425      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40426      */
40427     monitorPoll : 200,
40428
40429   
40430     /**
40431      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40432      * fields are added and the column is closed. If no fields are passed the column remains open
40433      * until end() is called.
40434      * @param {Object} config The config to pass to the column
40435      * @param {Field} field1 (optional)
40436      * @param {Field} field2 (optional)
40437      * @param {Field} etc (optional)
40438      * @return Column The column container object
40439      */
40440     column : function(c){
40441         var col = new Roo.form.Column(c);
40442         this.start(col);
40443         if(arguments.length > 1){ // duplicate code required because of Opera
40444             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40445             this.end();
40446         }
40447         return col;
40448     },
40449
40450     /**
40451      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40452      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40453      * until end() is called.
40454      * @param {Object} config The config to pass to the fieldset
40455      * @param {Field} field1 (optional)
40456      * @param {Field} field2 (optional)
40457      * @param {Field} etc (optional)
40458      * @return FieldSet The fieldset container object
40459      */
40460     fieldset : function(c){
40461         var fs = new Roo.form.FieldSet(c);
40462         this.start(fs);
40463         if(arguments.length > 1){ // duplicate code required because of Opera
40464             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40465             this.end();
40466         }
40467         return fs;
40468     },
40469
40470     /**
40471      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40472      * fields are added and the container is closed. If no fields are passed the container remains open
40473      * until end() is called.
40474      * @param {Object} config The config to pass to the Layout
40475      * @param {Field} field1 (optional)
40476      * @param {Field} field2 (optional)
40477      * @param {Field} etc (optional)
40478      * @return Layout The container object
40479      */
40480     container : function(c){
40481         var l = new Roo.form.Layout(c);
40482         this.start(l);
40483         if(arguments.length > 1){ // duplicate code required because of Opera
40484             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40485             this.end();
40486         }
40487         return l;
40488     },
40489
40490     /**
40491      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40492      * @param {Object} container A Roo.form.Layout or subclass of Layout
40493      * @return {Form} this
40494      */
40495     start : function(c){
40496         // cascade label info
40497         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40498         this.active.stack.push(c);
40499         c.ownerCt = this.active;
40500         this.active = c;
40501         return this;
40502     },
40503
40504     /**
40505      * Closes the current open container
40506      * @return {Form} this
40507      */
40508     end : function(){
40509         if(this.active == this.root){
40510             return this;
40511         }
40512         this.active = this.active.ownerCt;
40513         return this;
40514     },
40515
40516     /**
40517      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40518      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40519      * as the label of the field.
40520      * @param {Field} field1
40521      * @param {Field} field2 (optional)
40522      * @param {Field} etc. (optional)
40523      * @return {Form} this
40524      */
40525     add : function(){
40526         this.active.stack.push.apply(this.active.stack, arguments);
40527         this.allItems.push.apply(this.allItems,arguments);
40528         var r = [];
40529         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40530             if(a[i].isFormField){
40531                 r.push(a[i]);
40532             }
40533         }
40534         if(r.length > 0){
40535             Roo.form.Form.superclass.add.apply(this, r);
40536         }
40537         return this;
40538     },
40539     
40540
40541     
40542     
40543     
40544      /**
40545      * Find any element that has been added to a form, using it's ID or name
40546      * This can include framesets, columns etc. along with regular fields..
40547      * @param {String} id - id or name to find.
40548      
40549      * @return {Element} e - or false if nothing found.
40550      */
40551     findbyId : function(id)
40552     {
40553         var ret = false;
40554         if (!id) {
40555             return ret;
40556         }
40557         Ext.each(this.allItems, function(f){
40558             if (f.id == id || f.name == id ){
40559                 ret = f;
40560                 return false;
40561             }
40562         });
40563         return ret;
40564     },
40565
40566     
40567     
40568     /**
40569      * Render this form into the passed container. This should only be called once!
40570      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40571      * @return {Form} this
40572      */
40573     render : function(ct){
40574         ct = Roo.get(ct);
40575         var o = this.autoCreate || {
40576             tag: 'form',
40577             method : this.method || 'POST',
40578             id : this.id || Roo.id()
40579         };
40580         this.initEl(ct.createChild(o));
40581
40582         this.root.render(this.el);
40583
40584         this.items.each(function(f){
40585             f.render('x-form-el-'+f.id);
40586         });
40587
40588         if(this.buttons.length > 0){
40589             // tables are required to maintain order and for correct IE layout
40590             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40591                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40592                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40593             }}, null, true);
40594             var tr = tb.getElementsByTagName('tr')[0];
40595             for(var i = 0, len = this.buttons.length; i < len; i++) {
40596                 var b = this.buttons[i];
40597                 var td = document.createElement('td');
40598                 td.className = 'x-form-btn-td';
40599                 b.render(tr.appendChild(td));
40600             }
40601         }
40602         if(this.monitorValid){ // initialize after render
40603             this.startMonitoring();
40604         }
40605         this.fireEvent('rendered', this);
40606         return this;
40607     },
40608
40609     /**
40610      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40611      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40612      * object or a valid Roo.DomHelper element config
40613      * @param {Function} handler The function called when the button is clicked
40614      * @param {Object} scope (optional) The scope of the handler function
40615      * @return {Roo.Button}
40616      */
40617     addButton : function(config, handler, scope){
40618         var bc = {
40619             handler: handler,
40620             scope: scope,
40621             minWidth: this.minButtonWidth,
40622             hideParent:true
40623         };
40624         if(typeof config == "string"){
40625             bc.text = config;
40626         }else{
40627             Roo.apply(bc, config);
40628         }
40629         var btn = new Roo.Button(null, bc);
40630         this.buttons.push(btn);
40631         return btn;
40632     },
40633
40634      /**
40635      * Adds a series of form elements (using the xtype property as the factory method.
40636      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40637      * @param {Object} config 
40638      */
40639     
40640     addxtype : function()
40641     {
40642         var ar = Array.prototype.slice.call(arguments, 0);
40643         var ret = false;
40644         for(var i = 0; i < ar.length; i++) {
40645             if (!ar[i]) {
40646                 continue; // skip -- if this happends something invalid got sent, we 
40647                 // should ignore it, as basically that interface element will not show up
40648                 // and that should be pretty obvious!!
40649             }
40650             
40651             if (Roo.form[ar[i].xtype]) {
40652                 ar[i].form = this;
40653                 var fe = Roo.factory(ar[i], Roo.form);
40654                 if (!ret) {
40655                     ret = fe;
40656                 }
40657                 fe.form = this;
40658                 if (fe.store) {
40659                     fe.store.form = this;
40660                 }
40661                 if (fe.isLayout) {  
40662                          
40663                     this.start(fe);
40664                     this.allItems.push(fe);
40665                     if (fe.items && fe.addxtype) {
40666                         fe.addxtype.apply(fe, fe.items);
40667                         delete fe.items;
40668                     }
40669                      this.end();
40670                     continue;
40671                 }
40672                 
40673                 
40674                  
40675                 this.add(fe);
40676               //  console.log('adding ' + ar[i].xtype);
40677             }
40678             if (ar[i].xtype == 'Button') {  
40679                 //console.log('adding button');
40680                 //console.log(ar[i]);
40681                 this.addButton(ar[i]);
40682                 this.allItems.push(fe);
40683                 continue;
40684             }
40685             
40686             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40687                 alert('end is not supported on xtype any more, use items');
40688             //    this.end();
40689             //    //console.log('adding end');
40690             }
40691             
40692         }
40693         return ret;
40694     },
40695     
40696     /**
40697      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40698      * option "monitorValid"
40699      */
40700     startMonitoring : function(){
40701         if(!this.bound){
40702             this.bound = true;
40703             Roo.TaskMgr.start({
40704                 run : this.bindHandler,
40705                 interval : this.monitorPoll || 200,
40706                 scope: this
40707             });
40708         }
40709     },
40710
40711     /**
40712      * Stops monitoring of the valid state of this form
40713      */
40714     stopMonitoring : function(){
40715         this.bound = false;
40716     },
40717
40718     // private
40719     bindHandler : function(){
40720         if(!this.bound){
40721             return false; // stops binding
40722         }
40723         var valid = true;
40724         this.items.each(function(f){
40725             if(!f.isValid(true)){
40726                 valid = false;
40727                 return false;
40728             }
40729         });
40730         for(var i = 0, len = this.buttons.length; i < len; i++){
40731             var btn = this.buttons[i];
40732             if(btn.formBind === true && btn.disabled === valid){
40733                 btn.setDisabled(!valid);
40734             }
40735         }
40736         this.fireEvent('clientvalidation', this, valid);
40737     }
40738     
40739     
40740     
40741     
40742     
40743     
40744     
40745     
40746 });
40747
40748
40749 // back compat
40750 Roo.Form = Roo.form.Form;
40751 /*
40752  * Based on:
40753  * Ext JS Library 1.1.1
40754  * Copyright(c) 2006-2007, Ext JS, LLC.
40755  *
40756  * Originally Released Under LGPL - original licence link has changed is not relivant.
40757  *
40758  * Fork - LGPL
40759  * <script type="text/javascript">
40760  */
40761  
40762  /**
40763  * @class Roo.form.Action
40764  * Internal Class used to handle form actions
40765  * @constructor
40766  * @param {Roo.form.BasicForm} el The form element or its id
40767  * @param {Object} config Configuration options
40768  */
40769  
40770  
40771 // define the action interface
40772 Roo.form.Action = function(form, options){
40773     this.form = form;
40774     this.options = options || {};
40775 };
40776 /**
40777  * Client Validation Failed
40778  * @const 
40779  */
40780 Roo.form.Action.CLIENT_INVALID = 'client';
40781 /**
40782  * Server Validation Failed
40783  * @const 
40784  */
40785  Roo.form.Action.SERVER_INVALID = 'server';
40786  /**
40787  * Connect to Server Failed
40788  * @const 
40789  */
40790 Roo.form.Action.CONNECT_FAILURE = 'connect';
40791 /**
40792  * Reading Data from Server Failed
40793  * @const 
40794  */
40795 Roo.form.Action.LOAD_FAILURE = 'load';
40796
40797 Roo.form.Action.prototype = {
40798     type : 'default',
40799     failureType : undefined,
40800     response : undefined,
40801     result : undefined,
40802
40803     // interface method
40804     run : function(options){
40805
40806     },
40807
40808     // interface method
40809     success : function(response){
40810
40811     },
40812
40813     // interface method
40814     handleResponse : function(response){
40815
40816     },
40817
40818     // default connection failure
40819     failure : function(response){
40820         this.response = response;
40821         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40822         this.form.afterAction(this, false);
40823     },
40824
40825     processResponse : function(response){
40826         this.response = response;
40827         if(!response.responseText){
40828             return true;
40829         }
40830         this.result = this.handleResponse(response);
40831         return this.result;
40832     },
40833
40834     // utility functions used internally
40835     getUrl : function(appendParams){
40836         var url = this.options.url || this.form.url || this.form.el.dom.action;
40837         if(appendParams){
40838             var p = this.getParams();
40839             if(p){
40840                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40841             }
40842         }
40843         return url;
40844     },
40845
40846     getMethod : function(){
40847         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40848     },
40849
40850     getParams : function(){
40851         var bp = this.form.baseParams;
40852         var p = this.options.params;
40853         if(p){
40854             if(typeof p == "object"){
40855                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40856             }else if(typeof p == 'string' && bp){
40857                 p += '&' + Roo.urlEncode(bp);
40858             }
40859         }else if(bp){
40860             p = Roo.urlEncode(bp);
40861         }
40862         return p;
40863     },
40864
40865     createCallback : function(){
40866         return {
40867             success: this.success,
40868             failure: this.failure,
40869             scope: this,
40870             timeout: (this.form.timeout*1000),
40871             upload: this.form.fileUpload ? this.success : undefined
40872         };
40873     }
40874 };
40875
40876 Roo.form.Action.Submit = function(form, options){
40877     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40878 };
40879
40880 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40881     type : 'submit',
40882
40883     run : function()
40884     {
40885         // run get Values on the form, so it syncs any secondary forms.
40886         this.form.getValues();
40887         
40888         var o = this.options;
40889         var method = this.getMethod();
40890         var isPost = method == 'POST';
40891         if(o.clientValidation === false || this.form.isValid()){
40892             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40893                 form:this.form.el.dom,
40894                 url:this.getUrl(!isPost),
40895                 method: method,
40896                 params:isPost ? this.getParams() : null,
40897                 isUpload: this.form.fileUpload
40898             }));
40899
40900         }else if (o.clientValidation !== false){ // client validation failed
40901             this.failureType = Roo.form.Action.CLIENT_INVALID;
40902             this.form.afterAction(this, false);
40903         }
40904     },
40905
40906     success : function(response){
40907         var result = this.processResponse(response);
40908         if(result === true || result.success){
40909             this.form.afterAction(this, true);
40910             return;
40911         }
40912         if(result.errors){
40913             this.form.markInvalid(result.errors);
40914             this.failureType = Roo.form.Action.SERVER_INVALID;
40915         }
40916         this.form.afterAction(this, false);
40917     },
40918
40919     handleResponse : function(response){
40920         if(this.form.errorReader){
40921             var rs = this.form.errorReader.read(response);
40922             var errors = [];
40923             if(rs.records){
40924                 for(var i = 0, len = rs.records.length; i < len; i++) {
40925                     var r = rs.records[i];
40926                     errors[i] = r.data;
40927                 }
40928             }
40929             if(errors.length < 1){
40930                 errors = null;
40931             }
40932             return {
40933                 success : rs.success,
40934                 errors : errors
40935             };
40936         }
40937         var ret = false;
40938         try {
40939             ret = Roo.decode(response.responseText);
40940         } catch (e) {
40941             ret = {
40942                 success: false,
40943                 errorMsg: "Failed to read server message: " + response.responseText,
40944                 errors : []
40945             };
40946         }
40947         return ret;
40948         
40949     }
40950 });
40951
40952
40953 Roo.form.Action.Load = function(form, options){
40954     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40955     this.reader = this.form.reader;
40956 };
40957
40958 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40959     type : 'load',
40960
40961     run : function(){
40962         Roo.Ajax.request(Roo.apply(
40963                 this.createCallback(), {
40964                     method:this.getMethod(),
40965                     url:this.getUrl(false),
40966                     params:this.getParams()
40967         }));
40968     },
40969
40970     success : function(response){
40971         var result = this.processResponse(response);
40972         if(result === true || !result.success || !result.data){
40973             this.failureType = Roo.form.Action.LOAD_FAILURE;
40974             this.form.afterAction(this, false);
40975             return;
40976         }
40977         this.form.clearInvalid();
40978         this.form.setValues(result.data);
40979         this.form.afterAction(this, true);
40980     },
40981
40982     handleResponse : function(response){
40983         if(this.form.reader){
40984             var rs = this.form.reader.read(response);
40985             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40986             return {
40987                 success : rs.success,
40988                 data : data
40989             };
40990         }
40991         return Roo.decode(response.responseText);
40992     }
40993 });
40994
40995 Roo.form.Action.ACTION_TYPES = {
40996     'load' : Roo.form.Action.Load,
40997     'submit' : Roo.form.Action.Submit
40998 };/*
40999  * Based on:
41000  * Ext JS Library 1.1.1
41001  * Copyright(c) 2006-2007, Ext JS, LLC.
41002  *
41003  * Originally Released Under LGPL - original licence link has changed is not relivant.
41004  *
41005  * Fork - LGPL
41006  * <script type="text/javascript">
41007  */
41008  
41009 /**
41010  * @class Roo.form.Layout
41011  * @extends Roo.Component
41012  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41013  * @constructor
41014  * @param {Object} config Configuration options
41015  */
41016 Roo.form.Layout = function(config){
41017     var xitems = [];
41018     if (config.items) {
41019         xitems = config.items;
41020         delete config.items;
41021     }
41022     Roo.form.Layout.superclass.constructor.call(this, config);
41023     this.stack = [];
41024     Roo.each(xitems, this.addxtype, this);
41025      
41026 };
41027
41028 Roo.extend(Roo.form.Layout, Roo.Component, {
41029     /**
41030      * @cfg {String/Object} autoCreate
41031      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41032      */
41033     /**
41034      * @cfg {String/Object/Function} style
41035      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41036      * a function which returns such a specification.
41037      */
41038     /**
41039      * @cfg {String} labelAlign
41040      * Valid values are "left," "top" and "right" (defaults to "left")
41041      */
41042     /**
41043      * @cfg {Number} labelWidth
41044      * Fixed width in pixels of all field labels (defaults to undefined)
41045      */
41046     /**
41047      * @cfg {Boolean} clear
41048      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41049      */
41050     clear : true,
41051     /**
41052      * @cfg {String} labelSeparator
41053      * The separator to use after field labels (defaults to ':')
41054      */
41055     labelSeparator : ':',
41056     /**
41057      * @cfg {Boolean} hideLabels
41058      * True to suppress the display of field labels in this layout (defaults to false)
41059      */
41060     hideLabels : false,
41061
41062     // private
41063     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41064     
41065     isLayout : true,
41066     
41067     // private
41068     onRender : function(ct, position){
41069         if(this.el){ // from markup
41070             this.el = Roo.get(this.el);
41071         }else {  // generate
41072             var cfg = this.getAutoCreate();
41073             this.el = ct.createChild(cfg, position);
41074         }
41075         if(this.style){
41076             this.el.applyStyles(this.style);
41077         }
41078         if(this.labelAlign){
41079             this.el.addClass('x-form-label-'+this.labelAlign);
41080         }
41081         if(this.hideLabels){
41082             this.labelStyle = "display:none";
41083             this.elementStyle = "padding-left:0;";
41084         }else{
41085             if(typeof this.labelWidth == 'number'){
41086                 this.labelStyle = "width:"+this.labelWidth+"px;";
41087                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41088             }
41089             if(this.labelAlign == 'top'){
41090                 this.labelStyle = "width:auto;";
41091                 this.elementStyle = "padding-left:0;";
41092             }
41093         }
41094         var stack = this.stack;
41095         var slen = stack.length;
41096         if(slen > 0){
41097             if(!this.fieldTpl){
41098                 var t = new Roo.Template(
41099                     '<div class="x-form-item {5}">',
41100                         '<label for="{0}" style="{2}">{1}{4}</label>',
41101                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41102                         '</div>',
41103                     '</div><div class="x-form-clear-left"></div>'
41104                 );
41105                 t.disableFormats = true;
41106                 t.compile();
41107                 Roo.form.Layout.prototype.fieldTpl = t;
41108             }
41109             for(var i = 0; i < slen; i++) {
41110                 if(stack[i].isFormField){
41111                     this.renderField(stack[i]);
41112                 }else{
41113                     this.renderComponent(stack[i]);
41114                 }
41115             }
41116         }
41117         if(this.clear){
41118             this.el.createChild({cls:'x-form-clear'});
41119         }
41120     },
41121
41122     // private
41123     renderField : function(f){
41124         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41125                f.id, //0
41126                f.fieldLabel, //1
41127                f.labelStyle||this.labelStyle||'', //2
41128                this.elementStyle||'', //3
41129                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41130                f.itemCls||this.itemCls||''  //5
41131        ], true).getPrevSibling());
41132     },
41133
41134     // private
41135     renderComponent : function(c){
41136         c.render(c.isLayout ? this.el : this.el.createChild());    
41137     },
41138     /**
41139      * Adds a object form elements (using the xtype property as the factory method.)
41140      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41141      * @param {Object} config 
41142      */
41143     addxtype : function(o)
41144     {
41145         // create the lement.
41146         o.form = this.form;
41147         var fe = Roo.factory(o, Roo.form);
41148         this.form.allItems.push(fe);
41149         this.stack.push(fe);
41150         
41151         if (fe.isFormField) {
41152             this.form.items.add(fe);
41153         }
41154          
41155         return fe;
41156     }
41157 });
41158
41159 /**
41160  * @class Roo.form.Column
41161  * @extends Roo.form.Layout
41162  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41163  * @constructor
41164  * @param {Object} config Configuration options
41165  */
41166 Roo.form.Column = function(config){
41167     Roo.form.Column.superclass.constructor.call(this, config);
41168 };
41169
41170 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41171     /**
41172      * @cfg {Number/String} width
41173      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41174      */
41175     /**
41176      * @cfg {String/Object} autoCreate
41177      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41178      */
41179
41180     // private
41181     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41182
41183     // private
41184     onRender : function(ct, position){
41185         Roo.form.Column.superclass.onRender.call(this, ct, position);
41186         if(this.width){
41187             this.el.setWidth(this.width);
41188         }
41189     }
41190 });
41191
41192
41193 /**
41194  * @class Roo.form.Row
41195  * @extends Roo.form.Layout
41196  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41197  * @constructor
41198  * @param {Object} config Configuration options
41199  */
41200
41201  
41202 Roo.form.Row = function(config){
41203     Roo.form.Row.superclass.constructor.call(this, config);
41204 };
41205  
41206 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41207       /**
41208      * @cfg {Number/String} width
41209      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41210      */
41211     /**
41212      * @cfg {Number/String} height
41213      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41214      */
41215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41216     
41217     padWidth : 20,
41218     // private
41219     onRender : function(ct, position){
41220         //console.log('row render');
41221         if(!this.rowTpl){
41222             var t = new Roo.Template(
41223                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41224                     '<label for="{0}" style="{2}">{1}{4}</label>',
41225                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41226                     '</div>',
41227                 '</div>'
41228             );
41229             t.disableFormats = true;
41230             t.compile();
41231             Roo.form.Layout.prototype.rowTpl = t;
41232         }
41233         this.fieldTpl = this.rowTpl;
41234         
41235         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41236         var labelWidth = 100;
41237         
41238         if ((this.labelAlign != 'top')) {
41239             if (typeof this.labelWidth == 'number') {
41240                 labelWidth = this.labelWidth
41241             }
41242             this.padWidth =  20 + labelWidth;
41243             
41244         }
41245         
41246         Roo.form.Column.superclass.onRender.call(this, ct, position);
41247         if(this.width){
41248             this.el.setWidth(this.width);
41249         }
41250         if(this.height){
41251             this.el.setHeight(this.height);
41252         }
41253     },
41254     
41255     // private
41256     renderField : function(f){
41257         f.fieldEl = this.fieldTpl.append(this.el, [
41258                f.id, f.fieldLabel,
41259                f.labelStyle||this.labelStyle||'',
41260                this.elementStyle||'',
41261                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41262                f.itemCls||this.itemCls||'',
41263                f.width ? f.width + this.padWidth : 160 + this.padWidth
41264        ],true);
41265     }
41266 });
41267  
41268
41269 /**
41270  * @class Roo.form.FieldSet
41271  * @extends Roo.form.Layout
41272  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41273  * @constructor
41274  * @param {Object} config Configuration options
41275  */
41276 Roo.form.FieldSet = function(config){
41277     Roo.form.FieldSet.superclass.constructor.call(this, config);
41278 };
41279
41280 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41281     /**
41282      * @cfg {String} legend
41283      * The text to display as the legend for the FieldSet (defaults to '')
41284      */
41285     /**
41286      * @cfg {String/Object} autoCreate
41287      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41288      */
41289
41290     // private
41291     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41292
41293     // private
41294     onRender : function(ct, position){
41295         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41296         if(this.legend){
41297             this.setLegend(this.legend);
41298         }
41299     },
41300
41301     // private
41302     setLegend : function(text){
41303         if(this.rendered){
41304             this.el.child('legend').update(text);
41305         }
41306     }
41307 });/*
41308  * Based on:
41309  * Ext JS Library 1.1.1
41310  * Copyright(c) 2006-2007, Ext JS, LLC.
41311  *
41312  * Originally Released Under LGPL - original licence link has changed is not relivant.
41313  *
41314  * Fork - LGPL
41315  * <script type="text/javascript">
41316  */
41317 /**
41318  * @class Roo.form.VTypes
41319  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41320  * @singleton
41321  */
41322 Roo.form.VTypes = function(){
41323     // closure these in so they are only created once.
41324     var alpha = /^[a-zA-Z_]+$/;
41325     var alphanum = /^[a-zA-Z0-9_]+$/;
41326     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41327     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41328
41329     // All these messages and functions are configurable
41330     return {
41331         /**
41332          * The function used to validate email addresses
41333          * @param {String} value The email address
41334          */
41335         'email' : function(v){
41336             return email.test(v);
41337         },
41338         /**
41339          * The error text to display when the email validation function returns false
41340          * @type String
41341          */
41342         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41343         /**
41344          * The keystroke filter mask to be applied on email input
41345          * @type RegExp
41346          */
41347         'emailMask' : /[a-z0-9_\.\-@]/i,
41348
41349         /**
41350          * The function used to validate URLs
41351          * @param {String} value The URL
41352          */
41353         'url' : function(v){
41354             return url.test(v);
41355         },
41356         /**
41357          * The error text to display when the url validation function returns false
41358          * @type String
41359          */
41360         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41361         
41362         /**
41363          * The function used to validate alpha values
41364          * @param {String} value The value
41365          */
41366         'alpha' : function(v){
41367             return alpha.test(v);
41368         },
41369         /**
41370          * The error text to display when the alpha validation function returns false
41371          * @type String
41372          */
41373         'alphaText' : 'This field should only contain letters and _',
41374         /**
41375          * The keystroke filter mask to be applied on alpha input
41376          * @type RegExp
41377          */
41378         'alphaMask' : /[a-z_]/i,
41379
41380         /**
41381          * The function used to validate alphanumeric values
41382          * @param {String} value The value
41383          */
41384         'alphanum' : function(v){
41385             return alphanum.test(v);
41386         },
41387         /**
41388          * The error text to display when the alphanumeric validation function returns false
41389          * @type String
41390          */
41391         'alphanumText' : 'This field should only contain letters, numbers and _',
41392         /**
41393          * The keystroke filter mask to be applied on alphanumeric input
41394          * @type RegExp
41395          */
41396         'alphanumMask' : /[a-z0-9_]/i
41397     };
41398 }();//<script type="text/javascript">
41399
41400 /**
41401  * @class Roo.form.FCKeditor
41402  * @extends Roo.form.TextArea
41403  * Wrapper around the FCKEditor http://www.fckeditor.net
41404  * @constructor
41405  * Creates a new FCKeditor
41406  * @param {Object} config Configuration options
41407  */
41408 Roo.form.FCKeditor = function(config){
41409     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41410     this.addEvents({
41411          /**
41412          * @event editorinit
41413          * Fired when the editor is initialized - you can add extra handlers here..
41414          * @param {FCKeditor} this
41415          * @param {Object} the FCK object.
41416          */
41417         editorinit : true
41418     });
41419     
41420     
41421 };
41422 Roo.form.FCKeditor.editors = { };
41423 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41424 {
41425     //defaultAutoCreate : {
41426     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41427     //},
41428     // private
41429     /**
41430      * @cfg {Object} fck options - see fck manual for details.
41431      */
41432     fckconfig : false,
41433     
41434     /**
41435      * @cfg {Object} fck toolbar set (Basic or Default)
41436      */
41437     toolbarSet : 'Basic',
41438     /**
41439      * @cfg {Object} fck BasePath
41440      */ 
41441     basePath : '/fckeditor/',
41442     
41443     
41444     frame : false,
41445     
41446     value : '',
41447     
41448    
41449     onRender : function(ct, position)
41450     {
41451         if(!this.el){
41452             this.defaultAutoCreate = {
41453                 tag: "textarea",
41454                 style:"width:300px;height:60px;",
41455                 autocomplete: "off"
41456             };
41457         }
41458         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41459         /*
41460         if(this.grow){
41461             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41462             if(this.preventScrollbars){
41463                 this.el.setStyle("overflow", "hidden");
41464             }
41465             this.el.setHeight(this.growMin);
41466         }
41467         */
41468         //console.log('onrender' + this.getId() );
41469         Roo.form.FCKeditor.editors[this.getId()] = this;
41470          
41471
41472         this.replaceTextarea() ;
41473         
41474     },
41475     
41476     getEditor : function() {
41477         return this.fckEditor;
41478     },
41479     /**
41480      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41481      * @param {Mixed} value The value to set
41482      */
41483     
41484     
41485     setValue : function(value)
41486     {
41487         //console.log('setValue: ' + value);
41488         
41489         if(typeof(value) == 'undefined') { // not sure why this is happending...
41490             return;
41491         }
41492         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41493         
41494         //if(!this.el || !this.getEditor()) {
41495         //    this.value = value;
41496             //this.setValue.defer(100,this,[value]);    
41497         //    return;
41498         //} 
41499         
41500         if(!this.getEditor()) {
41501             return;
41502         }
41503         
41504         this.getEditor().SetData(value);
41505         
41506         //
41507
41508     },
41509
41510     /**
41511      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41512      * @return {Mixed} value The field value
41513      */
41514     getValue : function()
41515     {
41516         
41517         if (this.frame && this.frame.dom.style.display == 'none') {
41518             return Roo.form.FCKeditor.superclass.getValue.call(this);
41519         }
41520         
41521         if(!this.el || !this.getEditor()) {
41522            
41523            // this.getValue.defer(100,this); 
41524             return this.value;
41525         }
41526        
41527         
41528         var value=this.getEditor().GetData();
41529         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41530         return Roo.form.FCKeditor.superclass.getValue.call(this);
41531         
41532
41533     },
41534
41535     /**
41536      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41537      * @return {Mixed} value The field value
41538      */
41539     getRawValue : function()
41540     {
41541         if (this.frame && this.frame.dom.style.display == 'none') {
41542             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41543         }
41544         
41545         if(!this.el || !this.getEditor()) {
41546             //this.getRawValue.defer(100,this); 
41547             return this.value;
41548             return;
41549         }
41550         
41551         
41552         
41553         var value=this.getEditor().GetData();
41554         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41555         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41556          
41557     },
41558     
41559     setSize : function(w,h) {
41560         
41561         
41562         
41563         //if (this.frame && this.frame.dom.style.display == 'none') {
41564         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41565         //    return;
41566         //}
41567         //if(!this.el || !this.getEditor()) {
41568         //    this.setSize.defer(100,this, [w,h]); 
41569         //    return;
41570         //}
41571         
41572         
41573         
41574         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41575         
41576         this.frame.dom.setAttribute('width', w);
41577         this.frame.dom.setAttribute('height', h);
41578         this.frame.setSize(w,h);
41579         
41580     },
41581     
41582     toggleSourceEdit : function(value) {
41583         
41584       
41585          
41586         this.el.dom.style.display = value ? '' : 'none';
41587         this.frame.dom.style.display = value ?  'none' : '';
41588         
41589     },
41590     
41591     
41592     focus: function(tag)
41593     {
41594         if (this.frame.dom.style.display == 'none') {
41595             return Roo.form.FCKeditor.superclass.focus.call(this);
41596         }
41597         if(!this.el || !this.getEditor()) {
41598             this.focus.defer(100,this, [tag]); 
41599             return;
41600         }
41601         
41602         
41603         
41604         
41605         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41606         this.getEditor().Focus();
41607         if (tgs.length) {
41608             if (!this.getEditor().Selection.GetSelection()) {
41609                 this.focus.defer(100,this, [tag]); 
41610                 return;
41611             }
41612             
41613             
41614             var r = this.getEditor().EditorDocument.createRange();
41615             r.setStart(tgs[0],0);
41616             r.setEnd(tgs[0],0);
41617             this.getEditor().Selection.GetSelection().removeAllRanges();
41618             this.getEditor().Selection.GetSelection().addRange(r);
41619             this.getEditor().Focus();
41620         }
41621         
41622     },
41623     
41624     
41625     
41626     replaceTextarea : function()
41627     {
41628         if ( document.getElementById( this.getId() + '___Frame' ) )
41629             return ;
41630         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41631         //{
41632             // We must check the elements firstly using the Id and then the name.
41633         var oTextarea = document.getElementById( this.getId() );
41634         
41635         var colElementsByName = document.getElementsByName( this.getId() ) ;
41636          
41637         oTextarea.style.display = 'none' ;
41638
41639         if ( oTextarea.tabIndex ) {            
41640             this.TabIndex = oTextarea.tabIndex ;
41641         }
41642         
41643         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41644         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41645         this.frame = Roo.get(this.getId() + '___Frame')
41646     },
41647     
41648     _getConfigHtml : function()
41649     {
41650         var sConfig = '' ;
41651
41652         for ( var o in this.fckconfig ) {
41653             sConfig += sConfig.length > 0  ? '&amp;' : '';
41654             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41655         }
41656
41657         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41658     },
41659     
41660     
41661     _getIFrameHtml : function()
41662     {
41663         var sFile = 'fckeditor.html' ;
41664         /* no idea what this is about..
41665         try
41666         {
41667             if ( (/fcksource=true/i).test( window.top.location.search ) )
41668                 sFile = 'fckeditor.original.html' ;
41669         }
41670         catch (e) { 
41671         */
41672
41673         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41674         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41675         
41676         
41677         var html = '<iframe id="' + this.getId() +
41678             '___Frame" src="' + sLink +
41679             '" width="' + this.width +
41680             '" height="' + this.height + '"' +
41681             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41682             ' frameborder="0" scrolling="no"></iframe>' ;
41683
41684         return html ;
41685     },
41686     
41687     _insertHtmlBefore : function( html, element )
41688     {
41689         if ( element.insertAdjacentHTML )       {
41690             // IE
41691             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41692         } else { // Gecko
41693             var oRange = document.createRange() ;
41694             oRange.setStartBefore( element ) ;
41695             var oFragment = oRange.createContextualFragment( html );
41696             element.parentNode.insertBefore( oFragment, element ) ;
41697         }
41698     }
41699     
41700     
41701   
41702     
41703     
41704     
41705     
41706
41707 });
41708
41709 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41710
41711 function FCKeditor_OnComplete(editorInstance){
41712     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41713     f.fckEditor = editorInstance;
41714     //console.log("loaded");
41715     f.fireEvent('editorinit', f, editorInstance);
41716
41717   
41718
41719  
41720
41721
41722
41723
41724
41725
41726
41727
41728
41729
41730
41731
41732
41733
41734
41735 //<script type="text/javascript">
41736 /**
41737  * @class Roo.form.GridField
41738  * @extends Roo.form.Field
41739  * Embed a grid (or editable grid into a form)
41740  * STATUS ALPHA
41741  * @constructor
41742  * Creates a new GridField
41743  * @param {Object} config Configuration options
41744  */
41745 Roo.form.GridField = function(config){
41746     Roo.form.GridField.superclass.constructor.call(this, config);
41747      
41748 };
41749
41750 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41751     /**
41752      * @cfg {Number} width  - used to restrict width of grid..
41753      */
41754     width : 100,
41755     /**
41756      * @cfg {Number} height - used to restrict height of grid..
41757      */
41758     height : 50,
41759      /**
41760      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41761      */
41762     xgrid : false, 
41763     /**
41764      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41765      * {tag: "input", type: "checkbox", autocomplete: "off"})
41766      */
41767    // defaultAutoCreate : { tag: 'div' },
41768     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41769     /**
41770      * @cfg {String} addTitle Text to include for adding a title.
41771      */
41772     addTitle : false,
41773     //
41774     onResize : function(){
41775         Roo.form.Field.superclass.onResize.apply(this, arguments);
41776     },
41777
41778     initEvents : function(){
41779         // Roo.form.Checkbox.superclass.initEvents.call(this);
41780         // has no events...
41781        
41782     },
41783
41784
41785     getResizeEl : function(){
41786         return this.wrap;
41787     },
41788
41789     getPositionEl : function(){
41790         return this.wrap;
41791     },
41792
41793     // private
41794     onRender : function(ct, position){
41795         
41796         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41797         var style = this.style;
41798         delete this.style;
41799         
41800         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41801         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41802         this.viewEl = this.wrap.createChild({ tag: 'div' });
41803         if (style) {
41804             this.viewEl.applyStyles(style);
41805         }
41806         if (this.width) {
41807             this.viewEl.setWidth(this.width);
41808         }
41809         if (this.height) {
41810             this.viewEl.setHeight(this.height);
41811         }
41812         //if(this.inputValue !== undefined){
41813         //this.setValue(this.value);
41814         
41815         
41816         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41817         
41818         
41819         this.grid.render();
41820         this.grid.getDataSource().on('remove', this.refreshValue, this);
41821         this.grid.getDataSource().on('update', this.refreshValue, this);
41822         this.grid.on('afteredit', this.refreshValue, this);
41823  
41824     },
41825      
41826     
41827     /**
41828      * Sets the value of the item. 
41829      * @param {String} either an object  or a string..
41830      */
41831     setValue : function(v){
41832         //this.value = v;
41833         v = v || []; // empty set..
41834         // this does not seem smart - it really only affects memoryproxy grids..
41835         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41836             var ds = this.grid.getDataSource();
41837             // assumes a json reader..
41838             var data = {}
41839             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41840             ds.loadData( data);
41841         }
41842         Roo.form.GridField.superclass.setValue.call(this, v);
41843         this.refreshValue();
41844         // should load data in the grid really....
41845     },
41846     
41847     // private
41848     refreshValue: function() {
41849          var val = [];
41850         this.grid.getDataSource().each(function(r) {
41851             val.push(r.data);
41852         });
41853         this.el.dom.value = Roo.encode(val);
41854     }
41855     
41856      
41857     
41858     
41859 });/*
41860  * Based on:
41861  * Ext JS Library 1.1.1
41862  * Copyright(c) 2006-2007, Ext JS, LLC.
41863  *
41864  * Originally Released Under LGPL - original licence link has changed is not relivant.
41865  *
41866  * Fork - LGPL
41867  * <script type="text/javascript">
41868  */
41869 /**
41870  * @class Roo.form.DisplayField
41871  * @extends Roo.form.Field
41872  * A generic Field to display non-editable data.
41873  * @constructor
41874  * Creates a new Display Field item.
41875  * @param {Object} config Configuration options
41876  */
41877 Roo.form.DisplayField = function(config){
41878     Roo.form.DisplayField.superclass.constructor.call(this, config);
41879     
41880 };
41881
41882 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
41883     inputType:      'hidden',
41884     allowBlank:     true,
41885     readOnly:         true,
41886     
41887  
41888     /**
41889      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
41890      */
41891     focusClass : undefined,
41892     /**
41893      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
41894      */
41895     fieldClass: 'x-form-field',
41896     
41897      /**
41898      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
41899      */
41900     valueRenderer: undefined,
41901     
41902     width: 100,
41903     /**
41904      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41905      * {tag: "input", type: "checkbox", autocomplete: "off"})
41906      */
41907      
41908  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41909
41910     onResize : function(){
41911         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
41912         
41913     },
41914
41915     initEvents : function(){
41916         // Roo.form.Checkbox.superclass.initEvents.call(this);
41917         // has no events...
41918        
41919     },
41920
41921
41922     getResizeEl : function(){
41923         return this.wrap;
41924     },
41925
41926     getPositionEl : function(){
41927         return this.wrap;
41928     },
41929
41930     // private
41931     onRender : function(ct, position){
41932         
41933         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
41934         //if(this.inputValue !== undefined){
41935         this.wrap = this.el.wrap();
41936         
41937         this.viewEl = this.wrap.createChild({ tag: 'div'});
41938         
41939         if (this.bodyStyle) {
41940             this.viewEl.applyStyles(this.bodyStyle);
41941         }
41942         //this.viewEl.setStyle('padding', '2px');
41943         
41944         this.setValue(this.value);
41945         
41946     },
41947 /*
41948     // private
41949     initValue : Roo.emptyFn,
41950
41951   */
41952
41953         // private
41954     onClick : function(){
41955         
41956     },
41957
41958     /**
41959      * Sets the checked state of the checkbox.
41960      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
41961      */
41962     setValue : function(v){
41963         this.value = v;
41964         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
41965         // this might be called before we have a dom element..
41966         if (!this.viewEl) {
41967             return;
41968         }
41969         this.viewEl.dom.innerHTML = html;
41970         Roo.form.DisplayField.superclass.setValue.call(this, v);
41971
41972     }
41973 });//<script type="text/javasscript">
41974  
41975
41976 /**
41977  * @class Roo.DDView
41978  * A DnD enabled version of Roo.View.
41979  * @param {Element/String} container The Element in which to create the View.
41980  * @param {String} tpl The template string used to create the markup for each element of the View
41981  * @param {Object} config The configuration properties. These include all the config options of
41982  * {@link Roo.View} plus some specific to this class.<br>
41983  * <p>
41984  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41985  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41986  * <p>
41987  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41988 .x-view-drag-insert-above {
41989         border-top:1px dotted #3366cc;
41990 }
41991 .x-view-drag-insert-below {
41992         border-bottom:1px dotted #3366cc;
41993 }
41994 </code></pre>
41995  * 
41996  */
41997  
41998 Roo.DDView = function(container, tpl, config) {
41999     Roo.DDView.superclass.constructor.apply(this, arguments);
42000     this.getEl().setStyle("outline", "0px none");
42001     this.getEl().unselectable();
42002     if (this.dragGroup) {
42003                 this.setDraggable(this.dragGroup.split(","));
42004     }
42005     if (this.dropGroup) {
42006                 this.setDroppable(this.dropGroup.split(","));
42007     }
42008     if (this.deletable) {
42009         this.setDeletable();
42010     }
42011     this.isDirtyFlag = false;
42012         this.addEvents({
42013                 "drop" : true
42014         });
42015 };
42016
42017 Roo.extend(Roo.DDView, Roo.View, {
42018 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42019 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42020 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42021 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42022
42023         isFormField: true,
42024
42025         reset: Roo.emptyFn,
42026         
42027         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42028
42029         validate: function() {
42030                 return true;
42031         },
42032         
42033         destroy: function() {
42034                 this.purgeListeners();
42035                 this.getEl.removeAllListeners();
42036                 this.getEl().remove();
42037                 if (this.dragZone) {
42038                         if (this.dragZone.destroy) {
42039                                 this.dragZone.destroy();
42040                         }
42041                 }
42042                 if (this.dropZone) {
42043                         if (this.dropZone.destroy) {
42044                                 this.dropZone.destroy();
42045                         }
42046                 }
42047         },
42048
42049 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42050         getName: function() {
42051                 return this.name;
42052         },
42053
42054 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42055         setValue: function(v) {
42056                 if (!this.store) {
42057                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42058                 }
42059                 var data = {};
42060                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42061                 this.store.proxy = new Roo.data.MemoryProxy(data);
42062                 this.store.load();
42063         },
42064
42065 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42066         getValue: function() {
42067                 var result = '(';
42068                 this.store.each(function(rec) {
42069                         result += rec.id + ',';
42070                 });
42071                 return result.substr(0, result.length - 1) + ')';
42072         },
42073         
42074         getIds: function() {
42075                 var i = 0, result = new Array(this.store.getCount());
42076                 this.store.each(function(rec) {
42077                         result[i++] = rec.id;
42078                 });
42079                 return result;
42080         },
42081         
42082         isDirty: function() {
42083                 return this.isDirtyFlag;
42084         },
42085
42086 /**
42087  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42088  *      whole Element becomes the target, and this causes the drop gesture to append.
42089  */
42090     getTargetFromEvent : function(e) {
42091                 var target = e.getTarget();
42092                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42093                 target = target.parentNode;
42094                 }
42095                 if (!target) {
42096                         target = this.el.dom.lastChild || this.el.dom;
42097                 }
42098                 return target;
42099     },
42100
42101 /**
42102  *      Create the drag data which consists of an object which has the property "ddel" as
42103  *      the drag proxy element. 
42104  */
42105     getDragData : function(e) {
42106         var target = this.findItemFromChild(e.getTarget());
42107                 if(target) {
42108                         this.handleSelection(e);
42109                         var selNodes = this.getSelectedNodes();
42110             var dragData = {
42111                 source: this,
42112                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42113                 nodes: selNodes,
42114                 records: []
42115                         };
42116                         var selectedIndices = this.getSelectedIndexes();
42117                         for (var i = 0; i < selectedIndices.length; i++) {
42118                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42119                         }
42120                         if (selNodes.length == 1) {
42121                                 dragData.ddel = target.cloneNode(true); // the div element
42122                         } else {
42123                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42124                                 div.className = 'multi-proxy';
42125                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42126                                         div.appendChild(selNodes[i].cloneNode(true));
42127                                 }
42128                                 dragData.ddel = div;
42129                         }
42130             //console.log(dragData)
42131             //console.log(dragData.ddel.innerHTML)
42132                         return dragData;
42133                 }
42134         //console.log('nodragData')
42135                 return false;
42136     },
42137     
42138 /**     Specify to which ddGroup items in this DDView may be dragged. */
42139     setDraggable: function(ddGroup) {
42140         if (ddGroup instanceof Array) {
42141                 Roo.each(ddGroup, this.setDraggable, this);
42142                 return;
42143         }
42144         if (this.dragZone) {
42145                 this.dragZone.addToGroup(ddGroup);
42146         } else {
42147                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42148                                 containerScroll: true,
42149                                 ddGroup: ddGroup 
42150
42151                         });
42152 //                      Draggability implies selection. DragZone's mousedown selects the element.
42153                         if (!this.multiSelect) { this.singleSelect = true; }
42154
42155 //                      Wire the DragZone's handlers up to methods in *this*
42156                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42157                 }
42158     },
42159
42160 /**     Specify from which ddGroup this DDView accepts drops. */
42161     setDroppable: function(ddGroup) {
42162         if (ddGroup instanceof Array) {
42163                 Roo.each(ddGroup, this.setDroppable, this);
42164                 return;
42165         }
42166         if (this.dropZone) {
42167                 this.dropZone.addToGroup(ddGroup);
42168         } else {
42169                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42170                                 containerScroll: true,
42171                                 ddGroup: ddGroup
42172                         });
42173
42174 //                      Wire the DropZone's handlers up to methods in *this*
42175                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42176                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42177                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42178                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42179                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42180                 }
42181     },
42182
42183 /**     Decide whether to drop above or below a View node. */
42184     getDropPoint : function(e, n, dd){
42185         if (n == this.el.dom) { return "above"; }
42186                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42187                 var c = t + (b - t) / 2;
42188                 var y = Roo.lib.Event.getPageY(e);
42189                 if(y <= c) {
42190                         return "above";
42191                 }else{
42192                         return "below";
42193                 }
42194     },
42195
42196     onNodeEnter : function(n, dd, e, data){
42197                 return false;
42198     },
42199     
42200     onNodeOver : function(n, dd, e, data){
42201                 var pt = this.getDropPoint(e, n, dd);
42202                 // set the insert point style on the target node
42203                 var dragElClass = this.dropNotAllowed;
42204                 if (pt) {
42205                         var targetElClass;
42206                         if (pt == "above"){
42207                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42208                                 targetElClass = "x-view-drag-insert-above";
42209                         } else {
42210                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42211                                 targetElClass = "x-view-drag-insert-below";
42212                         }
42213                         if (this.lastInsertClass != targetElClass){
42214                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42215                                 this.lastInsertClass = targetElClass;
42216                         }
42217                 }
42218                 return dragElClass;
42219         },
42220
42221     onNodeOut : function(n, dd, e, data){
42222                 this.removeDropIndicators(n);
42223     },
42224
42225     onNodeDrop : function(n, dd, e, data){
42226         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42227                 return false;
42228         }
42229         var pt = this.getDropPoint(e, n, dd);
42230                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42231                 if (pt == "below") { insertAt++; }
42232                 for (var i = 0; i < data.records.length; i++) {
42233                         var r = data.records[i];
42234                         var dup = this.store.getById(r.id);
42235                         if (dup && (dd != this.dragZone)) {
42236                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42237                         } else {
42238                                 if (data.copy) {
42239                                         this.store.insert(insertAt++, r.copy());
42240                                 } else {
42241                                         data.source.isDirtyFlag = true;
42242                                         r.store.remove(r);
42243                                         this.store.insert(insertAt++, r);
42244                                 }
42245                                 this.isDirtyFlag = true;
42246                         }
42247                 }
42248                 this.dragZone.cachedTarget = null;
42249                 return true;
42250     },
42251
42252     removeDropIndicators : function(n){
42253                 if(n){
42254                         Roo.fly(n).removeClass([
42255                                 "x-view-drag-insert-above",
42256                                 "x-view-drag-insert-below"]);
42257                         this.lastInsertClass = "_noclass";
42258                 }
42259     },
42260
42261 /**
42262  *      Utility method. Add a delete option to the DDView's context menu.
42263  *      @param {String} imageUrl The URL of the "delete" icon image.
42264  */
42265         setDeletable: function(imageUrl) {
42266                 if (!this.singleSelect && !this.multiSelect) {
42267                         this.singleSelect = true;
42268                 }
42269                 var c = this.getContextMenu();
42270                 this.contextMenu.on("itemclick", function(item) {
42271                         switch (item.id) {
42272                                 case "delete":
42273                                         this.remove(this.getSelectedIndexes());
42274                                         break;
42275                         }
42276                 }, this);
42277                 this.contextMenu.add({
42278                         icon: imageUrl,
42279                         id: "delete",
42280                         text: 'Delete'
42281                 });
42282         },
42283         
42284 /**     Return the context menu for this DDView. */
42285         getContextMenu: function() {
42286                 if (!this.contextMenu) {
42287 //                      Create the View's context menu
42288                         this.contextMenu = new Roo.menu.Menu({
42289                                 id: this.id + "-contextmenu"
42290                         });
42291                         this.el.on("contextmenu", this.showContextMenu, this);
42292                 }
42293                 return this.contextMenu;
42294         },
42295         
42296         disableContextMenu: function() {
42297                 if (this.contextMenu) {
42298                         this.el.un("contextmenu", this.showContextMenu, this);
42299                 }
42300         },
42301
42302         showContextMenu: function(e, item) {
42303         item = this.findItemFromChild(e.getTarget());
42304                 if (item) {
42305                         e.stopEvent();
42306                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42307                         this.contextMenu.showAt(e.getXY());
42308             }
42309     },
42310
42311 /**
42312  *      Remove {@link Roo.data.Record}s at the specified indices.
42313  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42314  */
42315     remove: function(selectedIndices) {
42316                 selectedIndices = [].concat(selectedIndices);
42317                 for (var i = 0; i < selectedIndices.length; i++) {
42318                         var rec = this.store.getAt(selectedIndices[i]);
42319                         this.store.remove(rec);
42320                 }
42321     },
42322
42323 /**
42324  *      Double click fires the event, but also, if this is draggable, and there is only one other
42325  *      related DropZone, it transfers the selected node.
42326  */
42327     onDblClick : function(e){
42328         var item = this.findItemFromChild(e.getTarget());
42329         if(item){
42330             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42331                 return false;
42332             }
42333             if (this.dragGroup) {
42334                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42335                     while (targets.indexOf(this.dropZone) > -1) {
42336                             targets.remove(this.dropZone);
42337                                 }
42338                     if (targets.length == 1) {
42339                                         this.dragZone.cachedTarget = null;
42340                         var el = Roo.get(targets[0].getEl());
42341                         var box = el.getBox(true);
42342                         targets[0].onNodeDrop(el.dom, {
42343                                 target: el.dom,
42344                                 xy: [box.x, box.y + box.height - 1]
42345                         }, null, this.getDragData(e));
42346                     }
42347                 }
42348         }
42349     },
42350     
42351     handleSelection: function(e) {
42352                 this.dragZone.cachedTarget = null;
42353         var item = this.findItemFromChild(e.getTarget());
42354         if (!item) {
42355                 this.clearSelections(true);
42356                 return;
42357         }
42358                 if (item && (this.multiSelect || this.singleSelect)){
42359                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42360                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42361                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42362                                 this.unselect(item);
42363                         } else {
42364                                 this.select(item, this.multiSelect && e.ctrlKey);
42365                                 this.lastSelection = item;
42366                         }
42367                 }
42368     },
42369
42370     onItemClick : function(item, index, e){
42371                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42372                         return false;
42373                 }
42374                 return true;
42375     },
42376
42377     unselect : function(nodeInfo, suppressEvent){
42378                 var node = this.getNode(nodeInfo);
42379                 if(node && this.isSelected(node)){
42380                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42381                                 Roo.fly(node).removeClass(this.selectedClass);
42382                                 this.selections.remove(node);
42383                                 if(!suppressEvent){
42384                                         this.fireEvent("selectionchange", this, this.selections);
42385                                 }
42386                         }
42387                 }
42388     }
42389 });
42390 /*
42391  * Based on:
42392  * Ext JS Library 1.1.1
42393  * Copyright(c) 2006-2007, Ext JS, LLC.
42394  *
42395  * Originally Released Under LGPL - original licence link has changed is not relivant.
42396  *
42397  * Fork - LGPL
42398  * <script type="text/javascript">
42399  */
42400  
42401 /**
42402  * @class Roo.LayoutManager
42403  * @extends Roo.util.Observable
42404  * Base class for layout managers.
42405  */
42406 Roo.LayoutManager = function(container, config){
42407     Roo.LayoutManager.superclass.constructor.call(this);
42408     this.el = Roo.get(container);
42409     // ie scrollbar fix
42410     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42411         document.body.scroll = "no";
42412     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42413         this.el.position('relative');
42414     }
42415     this.id = this.el.id;
42416     this.el.addClass("x-layout-container");
42417     /** false to disable window resize monitoring @type Boolean */
42418     this.monitorWindowResize = true;
42419     this.regions = {};
42420     this.addEvents({
42421         /**
42422          * @event layout
42423          * Fires when a layout is performed. 
42424          * @param {Roo.LayoutManager} this
42425          */
42426         "layout" : true,
42427         /**
42428          * @event regionresized
42429          * Fires when the user resizes a region. 
42430          * @param {Roo.LayoutRegion} region The resized region
42431          * @param {Number} newSize The new size (width for east/west, height for north/south)
42432          */
42433         "regionresized" : true,
42434         /**
42435          * @event regioncollapsed
42436          * Fires when a region is collapsed. 
42437          * @param {Roo.LayoutRegion} region The collapsed region
42438          */
42439         "regioncollapsed" : true,
42440         /**
42441          * @event regionexpanded
42442          * Fires when a region is expanded.  
42443          * @param {Roo.LayoutRegion} region The expanded region
42444          */
42445         "regionexpanded" : true
42446     });
42447     this.updating = false;
42448     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42449 };
42450
42451 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42452     /**
42453      * Returns true if this layout is currently being updated
42454      * @return {Boolean}
42455      */
42456     isUpdating : function(){
42457         return this.updating; 
42458     },
42459     
42460     /**
42461      * Suspend the LayoutManager from doing auto-layouts while
42462      * making multiple add or remove calls
42463      */
42464     beginUpdate : function(){
42465         this.updating = true;    
42466     },
42467     
42468     /**
42469      * Restore auto-layouts and optionally disable the manager from performing a layout
42470      * @param {Boolean} noLayout true to disable a layout update 
42471      */
42472     endUpdate : function(noLayout){
42473         this.updating = false;
42474         if(!noLayout){
42475             this.layout();
42476         }    
42477     },
42478     
42479     layout: function(){
42480         
42481     },
42482     
42483     onRegionResized : function(region, newSize){
42484         this.fireEvent("regionresized", region, newSize);
42485         this.layout();
42486     },
42487     
42488     onRegionCollapsed : function(region){
42489         this.fireEvent("regioncollapsed", region);
42490     },
42491     
42492     onRegionExpanded : function(region){
42493         this.fireEvent("regionexpanded", region);
42494     },
42495         
42496     /**
42497      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42498      * performs box-model adjustments.
42499      * @return {Object} The size as an object {width: (the width), height: (the height)}
42500      */
42501     getViewSize : function(){
42502         var size;
42503         if(this.el.dom != document.body){
42504             size = this.el.getSize();
42505         }else{
42506             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42507         }
42508         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42509         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42510         return size;
42511     },
42512     
42513     /**
42514      * Returns the Element this layout is bound to.
42515      * @return {Roo.Element}
42516      */
42517     getEl : function(){
42518         return this.el;
42519     },
42520     
42521     /**
42522      * Returns the specified region.
42523      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42524      * @return {Roo.LayoutRegion}
42525      */
42526     getRegion : function(target){
42527         return this.regions[target.toLowerCase()];
42528     },
42529     
42530     onWindowResize : function(){
42531         if(this.monitorWindowResize){
42532             this.layout();
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.BorderLayout
42547  * @extends Roo.LayoutManager
42548  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42549  * please see: <br><br>
42550  * <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>
42551  * <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>
42552  * Example:
42553  <pre><code>
42554  var layout = new Roo.BorderLayout(document.body, {
42555     north: {
42556         initialSize: 25,
42557         titlebar: false
42558     },
42559     west: {
42560         split:true,
42561         initialSize: 200,
42562         minSize: 175,
42563         maxSize: 400,
42564         titlebar: true,
42565         collapsible: true
42566     },
42567     east: {
42568         split:true,
42569         initialSize: 202,
42570         minSize: 175,
42571         maxSize: 400,
42572         titlebar: true,
42573         collapsible: true
42574     },
42575     south: {
42576         split:true,
42577         initialSize: 100,
42578         minSize: 100,
42579         maxSize: 200,
42580         titlebar: true,
42581         collapsible: true
42582     },
42583     center: {
42584         titlebar: true,
42585         autoScroll:true,
42586         resizeTabs: true,
42587         minTabWidth: 50,
42588         preferredTabWidth: 150
42589     }
42590 });
42591
42592 // shorthand
42593 var CP = Roo.ContentPanel;
42594
42595 layout.beginUpdate();
42596 layout.add("north", new CP("north", "North"));
42597 layout.add("south", new CP("south", {title: "South", closable: true}));
42598 layout.add("west", new CP("west", {title: "West"}));
42599 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42600 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42601 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42602 layout.getRegion("center").showPanel("center1");
42603 layout.endUpdate();
42604 </code></pre>
42605
42606 <b>The container the layout is rendered into can be either the body element or any other element.
42607 If it is not the body element, the container needs to either be an absolute positioned element,
42608 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42609 the container size if it is not the body element.</b>
42610
42611 * @constructor
42612 * Create a new BorderLayout
42613 * @param {String/HTMLElement/Element} container The container this layout is bound to
42614 * @param {Object} config Configuration options
42615  */
42616 Roo.BorderLayout = function(container, config){
42617     config = config || {};
42618     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42619     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42620     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42621         var target = this.factory.validRegions[i];
42622         if(config[target]){
42623             this.addRegion(target, config[target]);
42624         }
42625     }
42626 };
42627
42628 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42629     /**
42630      * Creates and adds a new region if it doesn't already exist.
42631      * @param {String} target The target region key (north, south, east, west or center).
42632      * @param {Object} config The regions config object
42633      * @return {BorderLayoutRegion} The new region
42634      */
42635     addRegion : function(target, config){
42636         if(!this.regions[target]){
42637             var r = this.factory.create(target, this, config);
42638             this.bindRegion(target, r);
42639         }
42640         return this.regions[target];
42641     },
42642
42643     // private (kinda)
42644     bindRegion : function(name, r){
42645         this.regions[name] = r;
42646         r.on("visibilitychange", this.layout, this);
42647         r.on("paneladded", this.layout, this);
42648         r.on("panelremoved", this.layout, this);
42649         r.on("invalidated", this.layout, this);
42650         r.on("resized", this.onRegionResized, this);
42651         r.on("collapsed", this.onRegionCollapsed, this);
42652         r.on("expanded", this.onRegionExpanded, this);
42653     },
42654
42655     /**
42656      * Performs a layout update.
42657      */
42658     layout : function(){
42659         if(this.updating) return;
42660         var size = this.getViewSize();
42661         var w = size.width;
42662         var h = size.height;
42663         var centerW = w;
42664         var centerH = h;
42665         var centerY = 0;
42666         var centerX = 0;
42667         //var x = 0, y = 0;
42668
42669         var rs = this.regions;
42670         var north = rs["north"];
42671         var south = rs["south"]; 
42672         var west = rs["west"];
42673         var east = rs["east"];
42674         var center = rs["center"];
42675         //if(this.hideOnLayout){ // not supported anymore
42676             //c.el.setStyle("display", "none");
42677         //}
42678         if(north && north.isVisible()){
42679             var b = north.getBox();
42680             var m = north.getMargins();
42681             b.width = w - (m.left+m.right);
42682             b.x = m.left;
42683             b.y = m.top;
42684             centerY = b.height + b.y + m.bottom;
42685             centerH -= centerY;
42686             north.updateBox(this.safeBox(b));
42687         }
42688         if(south && south.isVisible()){
42689             var b = south.getBox();
42690             var m = south.getMargins();
42691             b.width = w - (m.left+m.right);
42692             b.x = m.left;
42693             var totalHeight = (b.height + m.top + m.bottom);
42694             b.y = h - totalHeight + m.top;
42695             centerH -= totalHeight;
42696             south.updateBox(this.safeBox(b));
42697         }
42698         if(west && west.isVisible()){
42699             var b = west.getBox();
42700             var m = west.getMargins();
42701             b.height = centerH - (m.top+m.bottom);
42702             b.x = m.left;
42703             b.y = centerY + m.top;
42704             var totalWidth = (b.width + m.left + m.right);
42705             centerX += totalWidth;
42706             centerW -= totalWidth;
42707             west.updateBox(this.safeBox(b));
42708         }
42709         if(east && east.isVisible()){
42710             var b = east.getBox();
42711             var m = east.getMargins();
42712             b.height = centerH - (m.top+m.bottom);
42713             var totalWidth = (b.width + m.left + m.right);
42714             b.x = w - totalWidth + m.left;
42715             b.y = centerY + m.top;
42716             centerW -= totalWidth;
42717             east.updateBox(this.safeBox(b));
42718         }
42719         if(center){
42720             var m = center.getMargins();
42721             var centerBox = {
42722                 x: centerX + m.left,
42723                 y: centerY + m.top,
42724                 width: centerW - (m.left+m.right),
42725                 height: centerH - (m.top+m.bottom)
42726             };
42727             //if(this.hideOnLayout){
42728                 //center.el.setStyle("display", "block");
42729             //}
42730             center.updateBox(this.safeBox(centerBox));
42731         }
42732         this.el.repaint();
42733         this.fireEvent("layout", this);
42734     },
42735
42736     // private
42737     safeBox : function(box){
42738         box.width = Math.max(0, box.width);
42739         box.height = Math.max(0, box.height);
42740         return box;
42741     },
42742
42743     /**
42744      * Adds a ContentPanel (or subclass) to this layout.
42745      * @param {String} target The target region key (north, south, east, west or center).
42746      * @param {Roo.ContentPanel} panel The panel to add
42747      * @return {Roo.ContentPanel} The added panel
42748      */
42749     add : function(target, panel){
42750          
42751         target = target.toLowerCase();
42752         return this.regions[target].add(panel);
42753     },
42754
42755     /**
42756      * Remove a ContentPanel (or subclass) to this layout.
42757      * @param {String} target The target region key (north, south, east, west or center).
42758      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42759      * @return {Roo.ContentPanel} The removed panel
42760      */
42761     remove : function(target, panel){
42762         target = target.toLowerCase();
42763         return this.regions[target].remove(panel);
42764     },
42765
42766     /**
42767      * Searches all regions for a panel with the specified id
42768      * @param {String} panelId
42769      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42770      */
42771     findPanel : function(panelId){
42772         var rs = this.regions;
42773         for(var target in rs){
42774             if(typeof rs[target] != "function"){
42775                 var p = rs[target].getPanel(panelId);
42776                 if(p){
42777                     return p;
42778                 }
42779             }
42780         }
42781         return null;
42782     },
42783
42784     /**
42785      * Searches all regions for a panel with the specified id and activates (shows) it.
42786      * @param {String/ContentPanel} panelId The panels id or the panel itself
42787      * @return {Roo.ContentPanel} The shown panel or null
42788      */
42789     showPanel : function(panelId) {
42790       var rs = this.regions;
42791       for(var target in rs){
42792          var r = rs[target];
42793          if(typeof r != "function"){
42794             if(r.hasPanel(panelId)){
42795                return r.showPanel(panelId);
42796             }
42797          }
42798       }
42799       return null;
42800    },
42801
42802    /**
42803      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42804      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42805      */
42806     restoreState : function(provider){
42807         if(!provider){
42808             provider = Roo.state.Manager;
42809         }
42810         var sm = new Roo.LayoutStateManager();
42811         sm.init(this, provider);
42812     },
42813
42814     /**
42815      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42816      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42817      * a valid ContentPanel config object.  Example:
42818      * <pre><code>
42819 // Create the main layout
42820 var layout = new Roo.BorderLayout('main-ct', {
42821     west: {
42822         split:true,
42823         minSize: 175,
42824         titlebar: true
42825     },
42826     center: {
42827         title:'Components'
42828     }
42829 }, 'main-ct');
42830
42831 // Create and add multiple ContentPanels at once via configs
42832 layout.batchAdd({
42833    west: {
42834        id: 'source-files',
42835        autoCreate:true,
42836        title:'Ext Source Files',
42837        autoScroll:true,
42838        fitToFrame:true
42839    },
42840    center : {
42841        el: cview,
42842        autoScroll:true,
42843        fitToFrame:true,
42844        toolbar: tb,
42845        resizeEl:'cbody'
42846    }
42847 });
42848 </code></pre>
42849      * @param {Object} regions An object containing ContentPanel configs by region name
42850      */
42851     batchAdd : function(regions){
42852         this.beginUpdate();
42853         for(var rname in regions){
42854             var lr = this.regions[rname];
42855             if(lr){
42856                 this.addTypedPanels(lr, regions[rname]);
42857             }
42858         }
42859         this.endUpdate();
42860     },
42861
42862     // private
42863     addTypedPanels : function(lr, ps){
42864         if(typeof ps == 'string'){
42865             lr.add(new Roo.ContentPanel(ps));
42866         }
42867         else if(ps instanceof Array){
42868             for(var i =0, len = ps.length; i < len; i++){
42869                 this.addTypedPanels(lr, ps[i]);
42870             }
42871         }
42872         else if(!ps.events){ // raw config?
42873             var el = ps.el;
42874             delete ps.el; // prevent conflict
42875             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42876         }
42877         else {  // panel object assumed!
42878             lr.add(ps);
42879         }
42880     },
42881     /**
42882      * Adds a xtype elements to the layout.
42883      * <pre><code>
42884
42885 layout.addxtype({
42886        xtype : 'ContentPanel',
42887        region: 'west',
42888        items: [ .... ]
42889    }
42890 );
42891
42892 layout.addxtype({
42893         xtype : 'NestedLayoutPanel',
42894         region: 'west',
42895         layout: {
42896            center: { },
42897            west: { }   
42898         },
42899         items : [ ... list of content panels or nested layout panels.. ]
42900    }
42901 );
42902 </code></pre>
42903      * @param {Object} cfg Xtype definition of item to add.
42904      */
42905     addxtype : function(cfg)
42906     {
42907         // basically accepts a pannel...
42908         // can accept a layout region..!?!?
42909        // console.log('BorderLayout add ' + cfg.xtype)
42910         
42911         if (!cfg.xtype.match(/Panel$/)) {
42912             return false;
42913         }
42914         var ret = false;
42915         var region = cfg.region;
42916         delete cfg.region;
42917         
42918           
42919         var xitems = [];
42920         if (cfg.items) {
42921             xitems = cfg.items;
42922             delete cfg.items;
42923         }
42924         
42925         
42926         switch(cfg.xtype) 
42927         {
42928             case 'ContentPanel':  // ContentPanel (el, cfg)
42929                 if(cfg.autoCreate) {
42930                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42931                 } else {
42932                     var el = this.el.createChild();
42933                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42934                 }
42935                 
42936                 this.add(region, ret);
42937                 break;
42938             
42939             
42940             case 'TreePanel': // our new panel!
42941                 cfg.el = this.el.createChild();
42942                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42943                 this.add(region, ret);
42944                 break;
42945             
42946             case 'NestedLayoutPanel': 
42947                 // create a new Layout (which is  a Border Layout...
42948                 var el = this.el.createChild();
42949                 var clayout = cfg.layout;
42950                 delete cfg.layout;
42951                 clayout.items   = clayout.items  || [];
42952                 // replace this exitems with the clayout ones..
42953                 xitems = clayout.items;
42954                  
42955                 
42956                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42957                     cfg.background = false;
42958                 }
42959                 var layout = new Roo.BorderLayout(el, clayout);
42960                 
42961                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42962                 //console.log('adding nested layout panel '  + cfg.toSource());
42963                 this.add(region, ret);
42964                 
42965                 break;
42966                 
42967             case 'GridPanel': 
42968             
42969                 // needs grid and region
42970                 
42971                 //var el = this.getRegion(region).el.createChild();
42972                 var el = this.el.createChild();
42973                 // create the grid first...
42974                 
42975                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42976                 delete cfg.grid;
42977                 if (region == 'center' && this.active ) {
42978                     cfg.background = false;
42979                 }
42980                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42981                 
42982                 this.add(region, ret);
42983                 if (cfg.background) {
42984                     ret.on('activate', function(gp) {
42985                         if (!gp.grid.rendered) {
42986                             gp.grid.render();
42987                         }
42988                     });
42989                 } else {
42990                     grid.render();
42991                 }
42992                 break;
42993            
42994                
42995                 
42996                 
42997             default: 
42998                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42999                 return;
43000              // GridPanel (grid, cfg)
43001             
43002         }
43003         this.beginUpdate();
43004         // add children..
43005         Roo.each(xitems, function(i)  {
43006             ret.addxtype(i);
43007         });
43008         this.endUpdate();
43009         return ret;
43010         
43011     }
43012 });
43013
43014 /**
43015  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43016  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43017  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43018  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43019  * <pre><code>
43020 // shorthand
43021 var CP = Roo.ContentPanel;
43022
43023 var layout = Roo.BorderLayout.create({
43024     north: {
43025         initialSize: 25,
43026         titlebar: false,
43027         panels: [new CP("north", "North")]
43028     },
43029     west: {
43030         split:true,
43031         initialSize: 200,
43032         minSize: 175,
43033         maxSize: 400,
43034         titlebar: true,
43035         collapsible: true,
43036         panels: [new CP("west", {title: "West"})]
43037     },
43038     east: {
43039         split:true,
43040         initialSize: 202,
43041         minSize: 175,
43042         maxSize: 400,
43043         titlebar: true,
43044         collapsible: true,
43045         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43046     },
43047     south: {
43048         split:true,
43049         initialSize: 100,
43050         minSize: 100,
43051         maxSize: 200,
43052         titlebar: true,
43053         collapsible: true,
43054         panels: [new CP("south", {title: "South", closable: true})]
43055     },
43056     center: {
43057         titlebar: true,
43058         autoScroll:true,
43059         resizeTabs: true,
43060         minTabWidth: 50,
43061         preferredTabWidth: 150,
43062         panels: [
43063             new CP("center1", {title: "Close Me", closable: true}),
43064             new CP("center2", {title: "Center Panel", closable: false})
43065         ]
43066     }
43067 }, document.body);
43068
43069 layout.getRegion("center").showPanel("center1");
43070 </code></pre>
43071  * @param config
43072  * @param targetEl
43073  */
43074 Roo.BorderLayout.create = function(config, targetEl){
43075     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43076     layout.beginUpdate();
43077     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43078     for(var j = 0, jlen = regions.length; j < jlen; j++){
43079         var lr = regions[j];
43080         if(layout.regions[lr] && config[lr].panels){
43081             var r = layout.regions[lr];
43082             var ps = config[lr].panels;
43083             layout.addTypedPanels(r, ps);
43084         }
43085     }
43086     layout.endUpdate();
43087     return layout;
43088 };
43089
43090 // private
43091 Roo.BorderLayout.RegionFactory = {
43092     // private
43093     validRegions : ["north","south","east","west","center"],
43094
43095     // private
43096     create : function(target, mgr, config){
43097         target = target.toLowerCase();
43098         if(config.lightweight || config.basic){
43099             return new Roo.BasicLayoutRegion(mgr, config, target);
43100         }
43101         switch(target){
43102             case "north":
43103                 return new Roo.NorthLayoutRegion(mgr, config);
43104             case "south":
43105                 return new Roo.SouthLayoutRegion(mgr, config);
43106             case "east":
43107                 return new Roo.EastLayoutRegion(mgr, config);
43108             case "west":
43109                 return new Roo.WestLayoutRegion(mgr, config);
43110             case "center":
43111                 return new Roo.CenterLayoutRegion(mgr, config);
43112         }
43113         throw 'Layout region "'+target+'" not supported.';
43114     }
43115 };/*
43116  * Based on:
43117  * Ext JS Library 1.1.1
43118  * Copyright(c) 2006-2007, Ext JS, LLC.
43119  *
43120  * Originally Released Under LGPL - original licence link has changed is not relivant.
43121  *
43122  * Fork - LGPL
43123  * <script type="text/javascript">
43124  */
43125  
43126 /**
43127  * @class Roo.BasicLayoutRegion
43128  * @extends Roo.util.Observable
43129  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43130  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43131  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43132  */
43133 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43134     this.mgr = mgr;
43135     this.position  = pos;
43136     this.events = {
43137         /**
43138          * @scope Roo.BasicLayoutRegion
43139          */
43140         
43141         /**
43142          * @event beforeremove
43143          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43144          * @param {Roo.LayoutRegion} this
43145          * @param {Roo.ContentPanel} panel The panel
43146          * @param {Object} e The cancel event object
43147          */
43148         "beforeremove" : true,
43149         /**
43150          * @event invalidated
43151          * Fires when the layout for this region is changed.
43152          * @param {Roo.LayoutRegion} this
43153          */
43154         "invalidated" : true,
43155         /**
43156          * @event visibilitychange
43157          * Fires when this region is shown or hidden 
43158          * @param {Roo.LayoutRegion} this
43159          * @param {Boolean} visibility true or false
43160          */
43161         "visibilitychange" : true,
43162         /**
43163          * @event paneladded
43164          * Fires when a panel is added. 
43165          * @param {Roo.LayoutRegion} this
43166          * @param {Roo.ContentPanel} panel The panel
43167          */
43168         "paneladded" : true,
43169         /**
43170          * @event panelremoved
43171          * Fires when a panel is removed. 
43172          * @param {Roo.LayoutRegion} this
43173          * @param {Roo.ContentPanel} panel The panel
43174          */
43175         "panelremoved" : true,
43176         /**
43177          * @event collapsed
43178          * Fires when this region is collapsed.
43179          * @param {Roo.LayoutRegion} this
43180          */
43181         "collapsed" : true,
43182         /**
43183          * @event expanded
43184          * Fires when this region is expanded.
43185          * @param {Roo.LayoutRegion} this
43186          */
43187         "expanded" : true,
43188         /**
43189          * @event slideshow
43190          * Fires when this region is slid into view.
43191          * @param {Roo.LayoutRegion} this
43192          */
43193         "slideshow" : true,
43194         /**
43195          * @event slidehide
43196          * Fires when this region slides out of view. 
43197          * @param {Roo.LayoutRegion} this
43198          */
43199         "slidehide" : true,
43200         /**
43201          * @event panelactivated
43202          * Fires when a panel is activated. 
43203          * @param {Roo.LayoutRegion} this
43204          * @param {Roo.ContentPanel} panel The activated panel
43205          */
43206         "panelactivated" : true,
43207         /**
43208          * @event resized
43209          * Fires when the user resizes this region. 
43210          * @param {Roo.LayoutRegion} this
43211          * @param {Number} newSize The new size (width for east/west, height for north/south)
43212          */
43213         "resized" : true
43214     };
43215     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43216     this.panels = new Roo.util.MixedCollection();
43217     this.panels.getKey = this.getPanelId.createDelegate(this);
43218     this.box = null;
43219     this.activePanel = null;
43220     // ensure listeners are added...
43221     
43222     if (config.listeners || config.events) {
43223         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43224             listeners : config.listeners || {},
43225             events : config.events || {}
43226         });
43227     }
43228     
43229     if(skipConfig !== true){
43230         this.applyConfig(config);
43231     }
43232 };
43233
43234 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43235     getPanelId : function(p){
43236         return p.getId();
43237     },
43238     
43239     applyConfig : function(config){
43240         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43241         this.config = config;
43242         
43243     },
43244     
43245     /**
43246      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43247      * the width, for horizontal (north, south) the height.
43248      * @param {Number} newSize The new width or height
43249      */
43250     resizeTo : function(newSize){
43251         var el = this.el ? this.el :
43252                  (this.activePanel ? this.activePanel.getEl() : null);
43253         if(el){
43254             switch(this.position){
43255                 case "east":
43256                 case "west":
43257                     el.setWidth(newSize);
43258                     this.fireEvent("resized", this, newSize);
43259                 break;
43260                 case "north":
43261                 case "south":
43262                     el.setHeight(newSize);
43263                     this.fireEvent("resized", this, newSize);
43264                 break;                
43265             }
43266         }
43267     },
43268     
43269     getBox : function(){
43270         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43271     },
43272     
43273     getMargins : function(){
43274         return this.margins;
43275     },
43276     
43277     updateBox : function(box){
43278         this.box = box;
43279         var el = this.activePanel.getEl();
43280         el.dom.style.left = box.x + "px";
43281         el.dom.style.top = box.y + "px";
43282         this.activePanel.setSize(box.width, box.height);
43283     },
43284     
43285     /**
43286      * Returns the container element for this region.
43287      * @return {Roo.Element}
43288      */
43289     getEl : function(){
43290         return this.activePanel;
43291     },
43292     
43293     /**
43294      * Returns true if this region is currently visible.
43295      * @return {Boolean}
43296      */
43297     isVisible : function(){
43298         return this.activePanel ? true : false;
43299     },
43300     
43301     setActivePanel : function(panel){
43302         panel = this.getPanel(panel);
43303         if(this.activePanel && this.activePanel != panel){
43304             this.activePanel.setActiveState(false);
43305             this.activePanel.getEl().setLeftTop(-10000,-10000);
43306         }
43307         this.activePanel = panel;
43308         panel.setActiveState(true);
43309         if(this.box){
43310             panel.setSize(this.box.width, this.box.height);
43311         }
43312         this.fireEvent("panelactivated", this, panel);
43313         this.fireEvent("invalidated");
43314     },
43315     
43316     /**
43317      * Show the specified panel.
43318      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43319      * @return {Roo.ContentPanel} The shown panel or null
43320      */
43321     showPanel : function(panel){
43322         if(panel = this.getPanel(panel)){
43323             this.setActivePanel(panel);
43324         }
43325         return panel;
43326     },
43327     
43328     /**
43329      * Get the active panel for this region.
43330      * @return {Roo.ContentPanel} The active panel or null
43331      */
43332     getActivePanel : function(){
43333         return this.activePanel;
43334     },
43335     
43336     /**
43337      * Add the passed ContentPanel(s)
43338      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43339      * @return {Roo.ContentPanel} The panel added (if only one was added)
43340      */
43341     add : function(panel){
43342         if(arguments.length > 1){
43343             for(var i = 0, len = arguments.length; i < len; i++) {
43344                 this.add(arguments[i]);
43345             }
43346             return null;
43347         }
43348         if(this.hasPanel(panel)){
43349             this.showPanel(panel);
43350             return panel;
43351         }
43352         var el = panel.getEl();
43353         if(el.dom.parentNode != this.mgr.el.dom){
43354             this.mgr.el.dom.appendChild(el.dom);
43355         }
43356         if(panel.setRegion){
43357             panel.setRegion(this);
43358         }
43359         this.panels.add(panel);
43360         el.setStyle("position", "absolute");
43361         if(!panel.background){
43362             this.setActivePanel(panel);
43363             if(this.config.initialSize && this.panels.getCount()==1){
43364                 this.resizeTo(this.config.initialSize);
43365             }
43366         }
43367         this.fireEvent("paneladded", this, panel);
43368         return panel;
43369     },
43370     
43371     /**
43372      * Returns true if the panel is in this region.
43373      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43374      * @return {Boolean}
43375      */
43376     hasPanel : function(panel){
43377         if(typeof panel == "object"){ // must be panel obj
43378             panel = panel.getId();
43379         }
43380         return this.getPanel(panel) ? true : false;
43381     },
43382     
43383     /**
43384      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43385      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43386      * @param {Boolean} preservePanel Overrides the config preservePanel option
43387      * @return {Roo.ContentPanel} The panel that was removed
43388      */
43389     remove : function(panel, preservePanel){
43390         panel = this.getPanel(panel);
43391         if(!panel){
43392             return null;
43393         }
43394         var e = {};
43395         this.fireEvent("beforeremove", this, panel, e);
43396         if(e.cancel === true){
43397             return null;
43398         }
43399         var panelId = panel.getId();
43400         this.panels.removeKey(panelId);
43401         return panel;
43402     },
43403     
43404     /**
43405      * Returns the panel specified or null if it's not in this region.
43406      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43407      * @return {Roo.ContentPanel}
43408      */
43409     getPanel : function(id){
43410         if(typeof id == "object"){ // must be panel obj
43411             return id;
43412         }
43413         return this.panels.get(id);
43414     },
43415     
43416     /**
43417      * Returns this regions position (north/south/east/west/center).
43418      * @return {String} 
43419      */
43420     getPosition: function(){
43421         return this.position;    
43422     }
43423 });/*
43424  * Based on:
43425  * Ext JS Library 1.1.1
43426  * Copyright(c) 2006-2007, Ext JS, LLC.
43427  *
43428  * Originally Released Under LGPL - original licence link has changed is not relivant.
43429  *
43430  * Fork - LGPL
43431  * <script type="text/javascript">
43432  */
43433  
43434 /**
43435  * @class Roo.LayoutRegion
43436  * @extends Roo.BasicLayoutRegion
43437  * This class represents a region in a layout manager.
43438  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43439  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43440  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43441  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43442  * @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})
43443  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43444  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43445  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43446  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43447  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43448  * @cfg {String} title The title for the region (overrides panel titles)
43449  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43450  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43451  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43452  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43453  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43454  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43455  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43456  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43457  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43458  * @cfg {Boolean} showPin True to show a pin button
43459 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43460 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43461 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43462 * @cfg {Number} width  For East/West panels
43463 * @cfg {Number} height For North/South panels
43464 * @cfg {Boolean} split To show the splitter
43465  */
43466 Roo.LayoutRegion = function(mgr, config, pos){
43467     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43468     var dh = Roo.DomHelper;
43469     /** This region's container element 
43470     * @type Roo.Element */
43471     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43472     /** This region's title element 
43473     * @type Roo.Element */
43474
43475     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43476         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43477         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43478     ]}, true);
43479     this.titleEl.enableDisplayMode();
43480     /** This region's title text element 
43481     * @type HTMLElement */
43482     this.titleTextEl = this.titleEl.dom.firstChild;
43483     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43484     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43485     this.closeBtn.enableDisplayMode();
43486     this.closeBtn.on("click", this.closeClicked, this);
43487     this.closeBtn.hide();
43488
43489     this.createBody(config);
43490     this.visible = true;
43491     this.collapsed = false;
43492
43493     if(config.hideWhenEmpty){
43494         this.hide();
43495         this.on("paneladded", this.validateVisibility, this);
43496         this.on("panelremoved", this.validateVisibility, this);
43497     }
43498     this.applyConfig(config);
43499 };
43500
43501 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43502
43503     createBody : function(){
43504         /** This region's body element 
43505         * @type Roo.Element */
43506         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43507     },
43508
43509     applyConfig : function(c){
43510         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43511             var dh = Roo.DomHelper;
43512             if(c.titlebar !== false){
43513                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43514                 this.collapseBtn.on("click", this.collapse, this);
43515                 this.collapseBtn.enableDisplayMode();
43516
43517                 if(c.showPin === true || this.showPin){
43518                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43519                     this.stickBtn.enableDisplayMode();
43520                     this.stickBtn.on("click", this.expand, this);
43521                     this.stickBtn.hide();
43522                 }
43523             }
43524             /** This region's collapsed element
43525             * @type Roo.Element */
43526             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43527                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43528             ]}, true);
43529             if(c.floatable !== false){
43530                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43531                this.collapsedEl.on("click", this.collapseClick, this);
43532             }
43533
43534             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43535                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43536                    id: "message", unselectable: "on", style:{"float":"left"}});
43537                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43538              }
43539             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43540             this.expandBtn.on("click", this.expand, this);
43541         }
43542         if(this.collapseBtn){
43543             this.collapseBtn.setVisible(c.collapsible == true);
43544         }
43545         this.cmargins = c.cmargins || this.cmargins ||
43546                          (this.position == "west" || this.position == "east" ?
43547                              {top: 0, left: 2, right:2, bottom: 0} :
43548                              {top: 2, left: 0, right:0, bottom: 2});
43549         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43550         this.bottomTabs = c.tabPosition != "top";
43551         this.autoScroll = c.autoScroll || false;
43552         if(this.autoScroll){
43553             this.bodyEl.setStyle("overflow", "auto");
43554         }else{
43555             this.bodyEl.setStyle("overflow", "hidden");
43556         }
43557         //if(c.titlebar !== false){
43558             if((!c.titlebar && !c.title) || c.titlebar === false){
43559                 this.titleEl.hide();
43560             }else{
43561                 this.titleEl.show();
43562                 if(c.title){
43563                     this.titleTextEl.innerHTML = c.title;
43564                 }
43565             }
43566         //}
43567         this.duration = c.duration || .30;
43568         this.slideDuration = c.slideDuration || .45;
43569         this.config = c;
43570         if(c.collapsed){
43571             this.collapse(true);
43572         }
43573         if(c.hidden){
43574             this.hide();
43575         }
43576     },
43577     /**
43578      * Returns true if this region is currently visible.
43579      * @return {Boolean}
43580      */
43581     isVisible : function(){
43582         return this.visible;
43583     },
43584
43585     /**
43586      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43587      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43588      */
43589     setCollapsedTitle : function(title){
43590         title = title || "&#160;";
43591         if(this.collapsedTitleTextEl){
43592             this.collapsedTitleTextEl.innerHTML = title;
43593         }
43594     },
43595
43596     getBox : function(){
43597         var b;
43598         if(!this.collapsed){
43599             b = this.el.getBox(false, true);
43600         }else{
43601             b = this.collapsedEl.getBox(false, true);
43602         }
43603         return b;
43604     },
43605
43606     getMargins : function(){
43607         return this.collapsed ? this.cmargins : this.margins;
43608     },
43609
43610     highlight : function(){
43611         this.el.addClass("x-layout-panel-dragover");
43612     },
43613
43614     unhighlight : function(){
43615         this.el.removeClass("x-layout-panel-dragover");
43616     },
43617
43618     updateBox : function(box){
43619         this.box = box;
43620         if(!this.collapsed){
43621             this.el.dom.style.left = box.x + "px";
43622             this.el.dom.style.top = box.y + "px";
43623             this.updateBody(box.width, box.height);
43624         }else{
43625             this.collapsedEl.dom.style.left = box.x + "px";
43626             this.collapsedEl.dom.style.top = box.y + "px";
43627             this.collapsedEl.setSize(box.width, box.height);
43628         }
43629         if(this.tabs){
43630             this.tabs.autoSizeTabs();
43631         }
43632     },
43633
43634     updateBody : function(w, h){
43635         if(w !== null){
43636             this.el.setWidth(w);
43637             w -= this.el.getBorderWidth("rl");
43638             if(this.config.adjustments){
43639                 w += this.config.adjustments[0];
43640             }
43641         }
43642         if(h !== null){
43643             this.el.setHeight(h);
43644             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43645             h -= this.el.getBorderWidth("tb");
43646             if(this.config.adjustments){
43647                 h += this.config.adjustments[1];
43648             }
43649             this.bodyEl.setHeight(h);
43650             if(this.tabs){
43651                 h = this.tabs.syncHeight(h);
43652             }
43653         }
43654         if(this.panelSize){
43655             w = w !== null ? w : this.panelSize.width;
43656             h = h !== null ? h : this.panelSize.height;
43657         }
43658         if(this.activePanel){
43659             var el = this.activePanel.getEl();
43660             w = w !== null ? w : el.getWidth();
43661             h = h !== null ? h : el.getHeight();
43662             this.panelSize = {width: w, height: h};
43663             this.activePanel.setSize(w, h);
43664         }
43665         if(Roo.isIE && this.tabs){
43666             this.tabs.el.repaint();
43667         }
43668     },
43669
43670     /**
43671      * Returns the container element for this region.
43672      * @return {Roo.Element}
43673      */
43674     getEl : function(){
43675         return this.el;
43676     },
43677
43678     /**
43679      * Hides this region.
43680      */
43681     hide : function(){
43682         if(!this.collapsed){
43683             this.el.dom.style.left = "-2000px";
43684             this.el.hide();
43685         }else{
43686             this.collapsedEl.dom.style.left = "-2000px";
43687             this.collapsedEl.hide();
43688         }
43689         this.visible = false;
43690         this.fireEvent("visibilitychange", this, false);
43691     },
43692
43693     /**
43694      * Shows this region if it was previously hidden.
43695      */
43696     show : function(){
43697         if(!this.collapsed){
43698             this.el.show();
43699         }else{
43700             this.collapsedEl.show();
43701         }
43702         this.visible = true;
43703         this.fireEvent("visibilitychange", this, true);
43704     },
43705
43706     closeClicked : function(){
43707         if(this.activePanel){
43708             this.remove(this.activePanel);
43709         }
43710     },
43711
43712     collapseClick : function(e){
43713         if(this.isSlid){
43714            e.stopPropagation();
43715            this.slideIn();
43716         }else{
43717            e.stopPropagation();
43718            this.slideOut();
43719         }
43720     },
43721
43722     /**
43723      * Collapses this region.
43724      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43725      */
43726     collapse : function(skipAnim){
43727         if(this.collapsed) return;
43728         this.collapsed = true;
43729         if(this.split){
43730             this.split.el.hide();
43731         }
43732         if(this.config.animate && skipAnim !== true){
43733             this.fireEvent("invalidated", this);
43734             this.animateCollapse();
43735         }else{
43736             this.el.setLocation(-20000,-20000);
43737             this.el.hide();
43738             this.collapsedEl.show();
43739             this.fireEvent("collapsed", this);
43740             this.fireEvent("invalidated", this);
43741         }
43742     },
43743
43744     animateCollapse : function(){
43745         // overridden
43746     },
43747
43748     /**
43749      * Expands this region if it was previously collapsed.
43750      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43751      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43752      */
43753     expand : function(e, skipAnim){
43754         if(e) e.stopPropagation();
43755         if(!this.collapsed || this.el.hasActiveFx()) return;
43756         if(this.isSlid){
43757             this.afterSlideIn();
43758             skipAnim = true;
43759         }
43760         this.collapsed = false;
43761         if(this.config.animate && skipAnim !== true){
43762             this.animateExpand();
43763         }else{
43764             this.el.show();
43765             if(this.split){
43766                 this.split.el.show();
43767             }
43768             this.collapsedEl.setLocation(-2000,-2000);
43769             this.collapsedEl.hide();
43770             this.fireEvent("invalidated", this);
43771             this.fireEvent("expanded", this);
43772         }
43773     },
43774
43775     animateExpand : function(){
43776         // overridden
43777     },
43778
43779     initTabs : function(){
43780         this.bodyEl.setStyle("overflow", "hidden");
43781         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43782             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43783             disableTooltips: this.config.disableTabTips
43784         });
43785         if(this.config.hideTabs){
43786             ts.stripWrap.setDisplayed(false);
43787         }
43788         this.tabs = ts;
43789         ts.resizeTabs = this.config.resizeTabs === true;
43790         ts.minTabWidth = this.config.minTabWidth || 40;
43791         ts.maxTabWidth = this.config.maxTabWidth || 250;
43792         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43793         ts.monitorResize = false;
43794         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43795         ts.bodyEl.addClass('x-layout-tabs-body');
43796         this.panels.each(this.initPanelAsTab, this);
43797     },
43798
43799     initPanelAsTab : function(panel){
43800         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43801                     this.config.closeOnTab && panel.isClosable());
43802         if(panel.tabTip !== undefined){
43803             ti.setTooltip(panel.tabTip);
43804         }
43805         ti.on("activate", function(){
43806               this.setActivePanel(panel);
43807         }, this);
43808         if(this.config.closeOnTab){
43809             ti.on("beforeclose", function(t, e){
43810                 e.cancel = true;
43811                 this.remove(panel);
43812             }, this);
43813         }
43814         return ti;
43815     },
43816
43817     updatePanelTitle : function(panel, title){
43818         if(this.activePanel == panel){
43819             this.updateTitle(title);
43820         }
43821         if(this.tabs){
43822             var ti = this.tabs.getTab(panel.getEl().id);
43823             ti.setText(title);
43824             if(panel.tabTip !== undefined){
43825                 ti.setTooltip(panel.tabTip);
43826             }
43827         }
43828     },
43829
43830     updateTitle : function(title){
43831         if(this.titleTextEl && !this.config.title){
43832             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43833         }
43834     },
43835
43836     setActivePanel : function(panel){
43837         panel = this.getPanel(panel);
43838         if(this.activePanel && this.activePanel != panel){
43839             this.activePanel.setActiveState(false);
43840         }
43841         this.activePanel = panel;
43842         panel.setActiveState(true);
43843         if(this.panelSize){
43844             panel.setSize(this.panelSize.width, this.panelSize.height);
43845         }
43846         if(this.closeBtn){
43847             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43848         }
43849         this.updateTitle(panel.getTitle());
43850         if(this.tabs){
43851             this.fireEvent("invalidated", this);
43852         }
43853         this.fireEvent("panelactivated", this, panel);
43854     },
43855
43856     /**
43857      * Shows the specified panel.
43858      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43859      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43860      */
43861     showPanel : function(panel){
43862         if(panel = this.getPanel(panel)){
43863             if(this.tabs){
43864                 var tab = this.tabs.getTab(panel.getEl().id);
43865                 if(tab.isHidden()){
43866                     this.tabs.unhideTab(tab.id);
43867                 }
43868                 tab.activate();
43869             }else{
43870                 this.setActivePanel(panel);
43871             }
43872         }
43873         return panel;
43874     },
43875
43876     /**
43877      * Get the active panel for this region.
43878      * @return {Roo.ContentPanel} The active panel or null
43879      */
43880     getActivePanel : function(){
43881         return this.activePanel;
43882     },
43883
43884     validateVisibility : function(){
43885         if(this.panels.getCount() < 1){
43886             this.updateTitle("&#160;");
43887             this.closeBtn.hide();
43888             this.hide();
43889         }else{
43890             if(!this.isVisible()){
43891                 this.show();
43892             }
43893         }
43894     },
43895
43896     /**
43897      * Adds the passed ContentPanel(s) to this region.
43898      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43899      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43900      */
43901     add : function(panel){
43902         if(arguments.length > 1){
43903             for(var i = 0, len = arguments.length; i < len; i++) {
43904                 this.add(arguments[i]);
43905             }
43906             return null;
43907         }
43908         if(this.hasPanel(panel)){
43909             this.showPanel(panel);
43910             return panel;
43911         }
43912         panel.setRegion(this);
43913         this.panels.add(panel);
43914         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43915             this.bodyEl.dom.appendChild(panel.getEl().dom);
43916             if(panel.background !== true){
43917                 this.setActivePanel(panel);
43918             }
43919             this.fireEvent("paneladded", this, panel);
43920             return panel;
43921         }
43922         if(!this.tabs){
43923             this.initTabs();
43924         }else{
43925             this.initPanelAsTab(panel);
43926         }
43927         if(panel.background !== true){
43928             this.tabs.activate(panel.getEl().id);
43929         }
43930         this.fireEvent("paneladded", this, panel);
43931         return panel;
43932     },
43933
43934     /**
43935      * Hides the tab for the specified panel.
43936      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43937      */
43938     hidePanel : function(panel){
43939         if(this.tabs && (panel = this.getPanel(panel))){
43940             this.tabs.hideTab(panel.getEl().id);
43941         }
43942     },
43943
43944     /**
43945      * Unhides the tab for a previously hidden panel.
43946      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43947      */
43948     unhidePanel : function(panel){
43949         if(this.tabs && (panel = this.getPanel(panel))){
43950             this.tabs.unhideTab(panel.getEl().id);
43951         }
43952     },
43953
43954     clearPanels : function(){
43955         while(this.panels.getCount() > 0){
43956              this.remove(this.panels.first());
43957         }
43958     },
43959
43960     /**
43961      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43962      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43963      * @param {Boolean} preservePanel Overrides the config preservePanel option
43964      * @return {Roo.ContentPanel} The panel that was removed
43965      */
43966     remove : function(panel, preservePanel){
43967         panel = this.getPanel(panel);
43968         if(!panel){
43969             return null;
43970         }
43971         var e = {};
43972         this.fireEvent("beforeremove", this, panel, e);
43973         if(e.cancel === true){
43974             return null;
43975         }
43976         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43977         var panelId = panel.getId();
43978         this.panels.removeKey(panelId);
43979         if(preservePanel){
43980             document.body.appendChild(panel.getEl().dom);
43981         }
43982         if(this.tabs){
43983             this.tabs.removeTab(panel.getEl().id);
43984         }else if (!preservePanel){
43985             this.bodyEl.dom.removeChild(panel.getEl().dom);
43986         }
43987         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43988             var p = this.panels.first();
43989             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43990             tempEl.appendChild(p.getEl().dom);
43991             this.bodyEl.update("");
43992             this.bodyEl.dom.appendChild(p.getEl().dom);
43993             tempEl = null;
43994             this.updateTitle(p.getTitle());
43995             this.tabs = null;
43996             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43997             this.setActivePanel(p);
43998         }
43999         panel.setRegion(null);
44000         if(this.activePanel == panel){
44001             this.activePanel = null;
44002         }
44003         if(this.config.autoDestroy !== false && preservePanel !== true){
44004             try{panel.destroy();}catch(e){}
44005         }
44006         this.fireEvent("panelremoved", this, panel);
44007         return panel;
44008     },
44009
44010     /**
44011      * Returns the TabPanel component used by this region
44012      * @return {Roo.TabPanel}
44013      */
44014     getTabs : function(){
44015         return this.tabs;
44016     },
44017
44018     createTool : function(parentEl, className){
44019         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44020             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44021         btn.addClassOnOver("x-layout-tools-button-over");
44022         return btn;
44023     }
44024 });/*
44025  * Based on:
44026  * Ext JS Library 1.1.1
44027  * Copyright(c) 2006-2007, Ext JS, LLC.
44028  *
44029  * Originally Released Under LGPL - original licence link has changed is not relivant.
44030  *
44031  * Fork - LGPL
44032  * <script type="text/javascript">
44033  */
44034  
44035
44036
44037 /**
44038  * @class Roo.SplitLayoutRegion
44039  * @extends Roo.LayoutRegion
44040  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44041  */
44042 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44043     this.cursor = cursor;
44044     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44045 };
44046
44047 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44048     splitTip : "Drag to resize.",
44049     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44050     useSplitTips : false,
44051
44052     applyConfig : function(config){
44053         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44054         if(config.split){
44055             if(!this.split){
44056                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44057                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44058                 /** The SplitBar for this region 
44059                 * @type Roo.SplitBar */
44060                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44061                 this.split.on("moved", this.onSplitMove, this);
44062                 this.split.useShim = config.useShim === true;
44063                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44064                 if(this.useSplitTips){
44065                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44066                 }
44067                 if(config.collapsible){
44068                     this.split.el.on("dblclick", this.collapse,  this);
44069                 }
44070             }
44071             if(typeof config.minSize != "undefined"){
44072                 this.split.minSize = config.minSize;
44073             }
44074             if(typeof config.maxSize != "undefined"){
44075                 this.split.maxSize = config.maxSize;
44076             }
44077             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44078                 this.hideSplitter();
44079             }
44080         }
44081     },
44082
44083     getHMaxSize : function(){
44084          var cmax = this.config.maxSize || 10000;
44085          var center = this.mgr.getRegion("center");
44086          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44087     },
44088
44089     getVMaxSize : function(){
44090          var cmax = this.config.maxSize || 10000;
44091          var center = this.mgr.getRegion("center");
44092          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44093     },
44094
44095     onSplitMove : function(split, newSize){
44096         this.fireEvent("resized", this, newSize);
44097     },
44098     
44099     /** 
44100      * Returns the {@link Roo.SplitBar} for this region.
44101      * @return {Roo.SplitBar}
44102      */
44103     getSplitBar : function(){
44104         return this.split;
44105     },
44106     
44107     hide : function(){
44108         this.hideSplitter();
44109         Roo.SplitLayoutRegion.superclass.hide.call(this);
44110     },
44111
44112     hideSplitter : function(){
44113         if(this.split){
44114             this.split.el.setLocation(-2000,-2000);
44115             this.split.el.hide();
44116         }
44117     },
44118
44119     show : function(){
44120         if(this.split){
44121             this.split.el.show();
44122         }
44123         Roo.SplitLayoutRegion.superclass.show.call(this);
44124     },
44125     
44126     beforeSlide: function(){
44127         if(Roo.isGecko){// firefox overflow auto bug workaround
44128             this.bodyEl.clip();
44129             if(this.tabs) this.tabs.bodyEl.clip();
44130             if(this.activePanel){
44131                 this.activePanel.getEl().clip();
44132                 
44133                 if(this.activePanel.beforeSlide){
44134                     this.activePanel.beforeSlide();
44135                 }
44136             }
44137         }
44138     },
44139     
44140     afterSlide : function(){
44141         if(Roo.isGecko){// firefox overflow auto bug workaround
44142             this.bodyEl.unclip();
44143             if(this.tabs) this.tabs.bodyEl.unclip();
44144             if(this.activePanel){
44145                 this.activePanel.getEl().unclip();
44146                 if(this.activePanel.afterSlide){
44147                     this.activePanel.afterSlide();
44148                 }
44149             }
44150         }
44151     },
44152
44153     initAutoHide : function(){
44154         if(this.autoHide !== false){
44155             if(!this.autoHideHd){
44156                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44157                 this.autoHideHd = {
44158                     "mouseout": function(e){
44159                         if(!e.within(this.el, true)){
44160                             st.delay(500);
44161                         }
44162                     },
44163                     "mouseover" : function(e){
44164                         st.cancel();
44165                     },
44166                     scope : this
44167                 };
44168             }
44169             this.el.on(this.autoHideHd);
44170         }
44171     },
44172
44173     clearAutoHide : function(){
44174         if(this.autoHide !== false){
44175             this.el.un("mouseout", this.autoHideHd.mouseout);
44176             this.el.un("mouseover", this.autoHideHd.mouseover);
44177         }
44178     },
44179
44180     clearMonitor : function(){
44181         Roo.get(document).un("click", this.slideInIf, this);
44182     },
44183
44184     // these names are backwards but not changed for compat
44185     slideOut : function(){
44186         if(this.isSlid || this.el.hasActiveFx()){
44187             return;
44188         }
44189         this.isSlid = true;
44190         if(this.collapseBtn){
44191             this.collapseBtn.hide();
44192         }
44193         this.closeBtnState = this.closeBtn.getStyle('display');
44194         this.closeBtn.hide();
44195         if(this.stickBtn){
44196             this.stickBtn.show();
44197         }
44198         this.el.show();
44199         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44200         this.beforeSlide();
44201         this.el.setStyle("z-index", 10001);
44202         this.el.slideIn(this.getSlideAnchor(), {
44203             callback: function(){
44204                 this.afterSlide();
44205                 this.initAutoHide();
44206                 Roo.get(document).on("click", this.slideInIf, this);
44207                 this.fireEvent("slideshow", this);
44208             },
44209             scope: this,
44210             block: true
44211         });
44212     },
44213
44214     afterSlideIn : function(){
44215         this.clearAutoHide();
44216         this.isSlid = false;
44217         this.clearMonitor();
44218         this.el.setStyle("z-index", "");
44219         if(this.collapseBtn){
44220             this.collapseBtn.show();
44221         }
44222         this.closeBtn.setStyle('display', this.closeBtnState);
44223         if(this.stickBtn){
44224             this.stickBtn.hide();
44225         }
44226         this.fireEvent("slidehide", this);
44227     },
44228
44229     slideIn : function(cb){
44230         if(!this.isSlid || this.el.hasActiveFx()){
44231             Roo.callback(cb);
44232             return;
44233         }
44234         this.isSlid = false;
44235         this.beforeSlide();
44236         this.el.slideOut(this.getSlideAnchor(), {
44237             callback: function(){
44238                 this.el.setLeftTop(-10000, -10000);
44239                 this.afterSlide();
44240                 this.afterSlideIn();
44241                 Roo.callback(cb);
44242             },
44243             scope: this,
44244             block: true
44245         });
44246     },
44247     
44248     slideInIf : function(e){
44249         if(!e.within(this.el)){
44250             this.slideIn();
44251         }
44252     },
44253
44254     animateCollapse : function(){
44255         this.beforeSlide();
44256         this.el.setStyle("z-index", 20000);
44257         var anchor = this.getSlideAnchor();
44258         this.el.slideOut(anchor, {
44259             callback : function(){
44260                 this.el.setStyle("z-index", "");
44261                 this.collapsedEl.slideIn(anchor, {duration:.3});
44262                 this.afterSlide();
44263                 this.el.setLocation(-10000,-10000);
44264                 this.el.hide();
44265                 this.fireEvent("collapsed", this);
44266             },
44267             scope: this,
44268             block: true
44269         });
44270     },
44271
44272     animateExpand : function(){
44273         this.beforeSlide();
44274         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44275         this.el.setStyle("z-index", 20000);
44276         this.collapsedEl.hide({
44277             duration:.1
44278         });
44279         this.el.slideIn(this.getSlideAnchor(), {
44280             callback : function(){
44281                 this.el.setStyle("z-index", "");
44282                 this.afterSlide();
44283                 if(this.split){
44284                     this.split.el.show();
44285                 }
44286                 this.fireEvent("invalidated", this);
44287                 this.fireEvent("expanded", this);
44288             },
44289             scope: this,
44290             block: true
44291         });
44292     },
44293
44294     anchors : {
44295         "west" : "left",
44296         "east" : "right",
44297         "north" : "top",
44298         "south" : "bottom"
44299     },
44300
44301     sanchors : {
44302         "west" : "l",
44303         "east" : "r",
44304         "north" : "t",
44305         "south" : "b"
44306     },
44307
44308     canchors : {
44309         "west" : "tl-tr",
44310         "east" : "tr-tl",
44311         "north" : "tl-bl",
44312         "south" : "bl-tl"
44313     },
44314
44315     getAnchor : function(){
44316         return this.anchors[this.position];
44317     },
44318
44319     getCollapseAnchor : function(){
44320         return this.canchors[this.position];
44321     },
44322
44323     getSlideAnchor : function(){
44324         return this.sanchors[this.position];
44325     },
44326
44327     getAlignAdj : function(){
44328         var cm = this.cmargins;
44329         switch(this.position){
44330             case "west":
44331                 return [0, 0];
44332             break;
44333             case "east":
44334                 return [0, 0];
44335             break;
44336             case "north":
44337                 return [0, 0];
44338             break;
44339             case "south":
44340                 return [0, 0];
44341             break;
44342         }
44343     },
44344
44345     getExpandAdj : function(){
44346         var c = this.collapsedEl, cm = this.cmargins;
44347         switch(this.position){
44348             case "west":
44349                 return [-(cm.right+c.getWidth()+cm.left), 0];
44350             break;
44351             case "east":
44352                 return [cm.right+c.getWidth()+cm.left, 0];
44353             break;
44354             case "north":
44355                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44356             break;
44357             case "south":
44358                 return [0, cm.top+cm.bottom+c.getHeight()];
44359             break;
44360         }
44361     }
44362 });/*
44363  * Based on:
44364  * Ext JS Library 1.1.1
44365  * Copyright(c) 2006-2007, Ext JS, LLC.
44366  *
44367  * Originally Released Under LGPL - original licence link has changed is not relivant.
44368  *
44369  * Fork - LGPL
44370  * <script type="text/javascript">
44371  */
44372 /*
44373  * These classes are private internal classes
44374  */
44375 Roo.CenterLayoutRegion = function(mgr, config){
44376     Roo.LayoutRegion.call(this, mgr, config, "center");
44377     this.visible = true;
44378     this.minWidth = config.minWidth || 20;
44379     this.minHeight = config.minHeight || 20;
44380 };
44381
44382 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44383     hide : function(){
44384         // center panel can't be hidden
44385     },
44386     
44387     show : function(){
44388         // center panel can't be hidden
44389     },
44390     
44391     getMinWidth: function(){
44392         return this.minWidth;
44393     },
44394     
44395     getMinHeight: function(){
44396         return this.minHeight;
44397     }
44398 });
44399
44400
44401 Roo.NorthLayoutRegion = function(mgr, config){
44402     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44403     if(this.split){
44404         this.split.placement = Roo.SplitBar.TOP;
44405         this.split.orientation = Roo.SplitBar.VERTICAL;
44406         this.split.el.addClass("x-layout-split-v");
44407     }
44408     var size = config.initialSize || config.height;
44409     if(typeof size != "undefined"){
44410         this.el.setHeight(size);
44411     }
44412 };
44413 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44414     orientation: Roo.SplitBar.VERTICAL,
44415     getBox : function(){
44416         if(this.collapsed){
44417             return this.collapsedEl.getBox();
44418         }
44419         var box = this.el.getBox();
44420         if(this.split){
44421             box.height += this.split.el.getHeight();
44422         }
44423         return box;
44424     },
44425     
44426     updateBox : function(box){
44427         if(this.split && !this.collapsed){
44428             box.height -= this.split.el.getHeight();
44429             this.split.el.setLeft(box.x);
44430             this.split.el.setTop(box.y+box.height);
44431             this.split.el.setWidth(box.width);
44432         }
44433         if(this.collapsed){
44434             this.updateBody(box.width, null);
44435         }
44436         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44437     }
44438 });
44439
44440 Roo.SouthLayoutRegion = function(mgr, config){
44441     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44442     if(this.split){
44443         this.split.placement = Roo.SplitBar.BOTTOM;
44444         this.split.orientation = Roo.SplitBar.VERTICAL;
44445         this.split.el.addClass("x-layout-split-v");
44446     }
44447     var size = config.initialSize || config.height;
44448     if(typeof size != "undefined"){
44449         this.el.setHeight(size);
44450     }
44451 };
44452 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44453     orientation: Roo.SplitBar.VERTICAL,
44454     getBox : function(){
44455         if(this.collapsed){
44456             return this.collapsedEl.getBox();
44457         }
44458         var box = this.el.getBox();
44459         if(this.split){
44460             var sh = this.split.el.getHeight();
44461             box.height += sh;
44462             box.y -= sh;
44463         }
44464         return box;
44465     },
44466     
44467     updateBox : function(box){
44468         if(this.split && !this.collapsed){
44469             var sh = this.split.el.getHeight();
44470             box.height -= sh;
44471             box.y += sh;
44472             this.split.el.setLeft(box.x);
44473             this.split.el.setTop(box.y-sh);
44474             this.split.el.setWidth(box.width);
44475         }
44476         if(this.collapsed){
44477             this.updateBody(box.width, null);
44478         }
44479         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44480     }
44481 });
44482
44483 Roo.EastLayoutRegion = function(mgr, config){
44484     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44485     if(this.split){
44486         this.split.placement = Roo.SplitBar.RIGHT;
44487         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44488         this.split.el.addClass("x-layout-split-h");
44489     }
44490     var size = config.initialSize || config.width;
44491     if(typeof size != "undefined"){
44492         this.el.setWidth(size);
44493     }
44494 };
44495 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44496     orientation: Roo.SplitBar.HORIZONTAL,
44497     getBox : function(){
44498         if(this.collapsed){
44499             return this.collapsedEl.getBox();
44500         }
44501         var box = this.el.getBox();
44502         if(this.split){
44503             var sw = this.split.el.getWidth();
44504             box.width += sw;
44505             box.x -= sw;
44506         }
44507         return box;
44508     },
44509
44510     updateBox : function(box){
44511         if(this.split && !this.collapsed){
44512             var sw = this.split.el.getWidth();
44513             box.width -= sw;
44514             this.split.el.setLeft(box.x);
44515             this.split.el.setTop(box.y);
44516             this.split.el.setHeight(box.height);
44517             box.x += sw;
44518         }
44519         if(this.collapsed){
44520             this.updateBody(null, box.height);
44521         }
44522         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44523     }
44524 });
44525
44526 Roo.WestLayoutRegion = function(mgr, config){
44527     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44528     if(this.split){
44529         this.split.placement = Roo.SplitBar.LEFT;
44530         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44531         this.split.el.addClass("x-layout-split-h");
44532     }
44533     var size = config.initialSize || config.width;
44534     if(typeof size != "undefined"){
44535         this.el.setWidth(size);
44536     }
44537 };
44538 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44539     orientation: Roo.SplitBar.HORIZONTAL,
44540     getBox : function(){
44541         if(this.collapsed){
44542             return this.collapsedEl.getBox();
44543         }
44544         var box = this.el.getBox();
44545         if(this.split){
44546             box.width += this.split.el.getWidth();
44547         }
44548         return box;
44549     },
44550     
44551     updateBox : function(box){
44552         if(this.split && !this.collapsed){
44553             var sw = this.split.el.getWidth();
44554             box.width -= sw;
44555             this.split.el.setLeft(box.x+box.width);
44556             this.split.el.setTop(box.y);
44557             this.split.el.setHeight(box.height);
44558         }
44559         if(this.collapsed){
44560             this.updateBody(null, box.height);
44561         }
44562         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44563     }
44564 });
44565 /*
44566  * Based on:
44567  * Ext JS Library 1.1.1
44568  * Copyright(c) 2006-2007, Ext JS, LLC.
44569  *
44570  * Originally Released Under LGPL - original licence link has changed is not relivant.
44571  *
44572  * Fork - LGPL
44573  * <script type="text/javascript">
44574  */
44575  
44576  
44577 /*
44578  * Private internal class for reading and applying state
44579  */
44580 Roo.LayoutStateManager = function(layout){
44581      // default empty state
44582      this.state = {
44583         north: {},
44584         south: {},
44585         east: {},
44586         west: {}       
44587     };
44588 };
44589
44590 Roo.LayoutStateManager.prototype = {
44591     init : function(layout, provider){
44592         this.provider = provider;
44593         var state = provider.get(layout.id+"-layout-state");
44594         if(state){
44595             var wasUpdating = layout.isUpdating();
44596             if(!wasUpdating){
44597                 layout.beginUpdate();
44598             }
44599             for(var key in state){
44600                 if(typeof state[key] != "function"){
44601                     var rstate = state[key];
44602                     var r = layout.getRegion(key);
44603                     if(r && rstate){
44604                         if(rstate.size){
44605                             r.resizeTo(rstate.size);
44606                         }
44607                         if(rstate.collapsed == true){
44608                             r.collapse(true);
44609                         }else{
44610                             r.expand(null, true);
44611                         }
44612                     }
44613                 }
44614             }
44615             if(!wasUpdating){
44616                 layout.endUpdate();
44617             }
44618             this.state = state; 
44619         }
44620         this.layout = layout;
44621         layout.on("regionresized", this.onRegionResized, this);
44622         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44623         layout.on("regionexpanded", this.onRegionExpanded, this);
44624     },
44625     
44626     storeState : function(){
44627         this.provider.set(this.layout.id+"-layout-state", this.state);
44628     },
44629     
44630     onRegionResized : function(region, newSize){
44631         this.state[region.getPosition()].size = newSize;
44632         this.storeState();
44633     },
44634     
44635     onRegionCollapsed : function(region){
44636         this.state[region.getPosition()].collapsed = true;
44637         this.storeState();
44638     },
44639     
44640     onRegionExpanded : function(region){
44641         this.state[region.getPosition()].collapsed = false;
44642         this.storeState();
44643     }
44644 };/*
44645  * Based on:
44646  * Ext JS Library 1.1.1
44647  * Copyright(c) 2006-2007, Ext JS, LLC.
44648  *
44649  * Originally Released Under LGPL - original licence link has changed is not relivant.
44650  *
44651  * Fork - LGPL
44652  * <script type="text/javascript">
44653  */
44654 /**
44655  * @class Roo.ContentPanel
44656  * @extends Roo.util.Observable
44657  * A basic ContentPanel element.
44658  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44659  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44660  * @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
44661  * @cfg {Boolean} closable True if the panel can be closed/removed
44662  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44663  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44664  * @cfg {Toolbar} toolbar A toolbar for this panel
44665  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44666  * @cfg {String} title The title for this panel
44667  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44668  * @cfg {String} url Calls {@link #setUrl} with this value
44669  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44670  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44671  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44672  * @constructor
44673  * Create a new ContentPanel.
44674  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44675  * @param {String/Object} config A string to set only the title or a config object
44676  * @param {String} content (optional) Set the HTML content for this panel
44677  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44678  */
44679 Roo.ContentPanel = function(el, config, content){
44680     
44681      
44682     /*
44683     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44684         config = el;
44685         el = Roo.id();
44686     }
44687     if (config && config.parentLayout) { 
44688         el = config.parentLayout.el.createChild(); 
44689     }
44690     */
44691     if(el.autoCreate){ // xtype is available if this is called from factory
44692         config = el;
44693         el = Roo.id();
44694     }
44695     this.el = Roo.get(el);
44696     if(!this.el && config && config.autoCreate){
44697         if(typeof config.autoCreate == "object"){
44698             if(!config.autoCreate.id){
44699                 config.autoCreate.id = config.id||el;
44700             }
44701             this.el = Roo.DomHelper.append(document.body,
44702                         config.autoCreate, true);
44703         }else{
44704             this.el = Roo.DomHelper.append(document.body,
44705                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44706         }
44707     }
44708     this.closable = false;
44709     this.loaded = false;
44710     this.active = false;
44711     if(typeof config == "string"){
44712         this.title = config;
44713     }else{
44714         Roo.apply(this, config);
44715     }
44716     
44717     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44718         this.wrapEl = this.el.wrap();    
44719         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44720         
44721     }
44722     
44723     
44724     
44725     if(this.resizeEl){
44726         this.resizeEl = Roo.get(this.resizeEl, true);
44727     }else{
44728         this.resizeEl = this.el;
44729     }
44730     this.addEvents({
44731         /**
44732          * @event activate
44733          * Fires when this panel is activated. 
44734          * @param {Roo.ContentPanel} this
44735          */
44736         "activate" : true,
44737         /**
44738          * @event deactivate
44739          * Fires when this panel is activated. 
44740          * @param {Roo.ContentPanel} this
44741          */
44742         "deactivate" : true,
44743
44744         /**
44745          * @event resize
44746          * Fires when this panel is resized if fitToFrame is true.
44747          * @param {Roo.ContentPanel} this
44748          * @param {Number} width The width after any component adjustments
44749          * @param {Number} height The height after any component adjustments
44750          */
44751         "resize" : true
44752     });
44753     if(this.autoScroll){
44754         this.resizeEl.setStyle("overflow", "auto");
44755     }
44756     content = content || this.content;
44757     if(content){
44758         this.setContent(content);
44759     }
44760     if(config && config.url){
44761         this.setUrl(this.url, this.params, this.loadOnce);
44762     }
44763     
44764     
44765     
44766     Roo.ContentPanel.superclass.constructor.call(this);
44767 };
44768
44769 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44770     tabTip:'',
44771     setRegion : function(region){
44772         this.region = region;
44773         if(region){
44774            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44775         }else{
44776            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44777         } 
44778     },
44779     
44780     /**
44781      * Returns the toolbar for this Panel if one was configured. 
44782      * @return {Roo.Toolbar} 
44783      */
44784     getToolbar : function(){
44785         return this.toolbar;
44786     },
44787     
44788     setActiveState : function(active){
44789         this.active = active;
44790         if(!active){
44791             this.fireEvent("deactivate", this);
44792         }else{
44793             this.fireEvent("activate", this);
44794         }
44795     },
44796     /**
44797      * Updates this panel's element
44798      * @param {String} content The new content
44799      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44800     */
44801     setContent : function(content, loadScripts){
44802         this.el.update(content, loadScripts);
44803     },
44804
44805     ignoreResize : function(w, h){
44806         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44807             return true;
44808         }else{
44809             this.lastSize = {width: w, height: h};
44810             return false;
44811         }
44812     },
44813     /**
44814      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44815      * @return {Roo.UpdateManager} The UpdateManager
44816      */
44817     getUpdateManager : function(){
44818         return this.el.getUpdateManager();
44819     },
44820      /**
44821      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44822      * @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:
44823 <pre><code>
44824 panel.load({
44825     url: "your-url.php",
44826     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44827     callback: yourFunction,
44828     scope: yourObject, //(optional scope)
44829     discardUrl: false,
44830     nocache: false,
44831     text: "Loading...",
44832     timeout: 30,
44833     scripts: false
44834 });
44835 </code></pre>
44836      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44837      * 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.
44838      * @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}
44839      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44840      * @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.
44841      * @return {Roo.ContentPanel} this
44842      */
44843     load : function(){
44844         var um = this.el.getUpdateManager();
44845         um.update.apply(um, arguments);
44846         return this;
44847     },
44848
44849
44850     /**
44851      * 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.
44852      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44853      * @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)
44854      * @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)
44855      * @return {Roo.UpdateManager} The UpdateManager
44856      */
44857     setUrl : function(url, params, loadOnce){
44858         if(this.refreshDelegate){
44859             this.removeListener("activate", this.refreshDelegate);
44860         }
44861         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44862         this.on("activate", this.refreshDelegate);
44863         return this.el.getUpdateManager();
44864     },
44865     
44866     _handleRefresh : function(url, params, loadOnce){
44867         if(!loadOnce || !this.loaded){
44868             var updater = this.el.getUpdateManager();
44869             updater.update(url, params, this._setLoaded.createDelegate(this));
44870         }
44871     },
44872     
44873     _setLoaded : function(){
44874         this.loaded = true;
44875     }, 
44876     
44877     /**
44878      * Returns this panel's id
44879      * @return {String} 
44880      */
44881     getId : function(){
44882         return this.el.id;
44883     },
44884     
44885     /** 
44886      * Returns this panel's element - used by regiosn to add.
44887      * @return {Roo.Element} 
44888      */
44889     getEl : function(){
44890         return this.wrapEl || this.el;
44891     },
44892     
44893     adjustForComponents : function(width, height){
44894         if(this.resizeEl != this.el){
44895             width -= this.el.getFrameWidth('lr');
44896             height -= this.el.getFrameWidth('tb');
44897         }
44898         if(this.toolbar){
44899             var te = this.toolbar.getEl();
44900             height -= te.getHeight();
44901             te.setWidth(width);
44902         }
44903         if(this.adjustments){
44904             width += this.adjustments[0];
44905             height += this.adjustments[1];
44906         }
44907         return {"width": width, "height": height};
44908     },
44909     
44910     setSize : function(width, height){
44911         if(this.fitToFrame && !this.ignoreResize(width, height)){
44912             if(this.fitContainer && this.resizeEl != this.el){
44913                 this.el.setSize(width, height);
44914             }
44915             var size = this.adjustForComponents(width, height);
44916             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44917             this.fireEvent('resize', this, size.width, size.height);
44918         }
44919     },
44920     
44921     /**
44922      * Returns this panel's title
44923      * @return {String} 
44924      */
44925     getTitle : function(){
44926         return this.title;
44927     },
44928     
44929     /**
44930      * Set this panel's title
44931      * @param {String} title
44932      */
44933     setTitle : function(title){
44934         this.title = title;
44935         if(this.region){
44936             this.region.updatePanelTitle(this, title);
44937         }
44938     },
44939     
44940     /**
44941      * Returns true is this panel was configured to be closable
44942      * @return {Boolean} 
44943      */
44944     isClosable : function(){
44945         return this.closable;
44946     },
44947     
44948     beforeSlide : function(){
44949         this.el.clip();
44950         this.resizeEl.clip();
44951     },
44952     
44953     afterSlide : function(){
44954         this.el.unclip();
44955         this.resizeEl.unclip();
44956     },
44957     
44958     /**
44959      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44960      *   Will fail silently if the {@link #setUrl} method has not been called.
44961      *   This does not activate the panel, just updates its content.
44962      */
44963     refresh : function(){
44964         if(this.refreshDelegate){
44965            this.loaded = false;
44966            this.refreshDelegate();
44967         }
44968     },
44969     
44970     /**
44971      * Destroys this panel
44972      */
44973     destroy : function(){
44974         this.el.removeAllListeners();
44975         var tempEl = document.createElement("span");
44976         tempEl.appendChild(this.el.dom);
44977         tempEl.innerHTML = "";
44978         this.el.remove();
44979         this.el = null;
44980     },
44981     
44982       /**
44983      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44984      * <pre><code>
44985
44986 layout.addxtype({
44987        xtype : 'Form',
44988        items: [ .... ]
44989    }
44990 );
44991
44992 </code></pre>
44993      * @param {Object} cfg Xtype definition of item to add.
44994      */
44995     
44996     addxtype : function(cfg) {
44997         // add form..
44998         if (cfg.xtype.match(/^Form$/)) {
44999             var el = this.el.createChild();
45000
45001             this.form = new  Roo.form.Form(cfg);
45002             
45003             
45004             if ( this.form.allItems.length) this.form.render(el.dom);
45005             return this.form;
45006         }
45007         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45008             // views..
45009             cfg.el = this.el.appendChild(document.createElement("div"));
45010             // factory?
45011             var ret = new Roo[cfg.xtype](cfg);
45012             ret.render(false, ''); // render blank..
45013             return ret;
45014             
45015         }
45016         return false;
45017         
45018     }
45019 });
45020
45021 /**
45022  * @class Roo.GridPanel
45023  * @extends Roo.ContentPanel
45024  * @constructor
45025  * Create a new GridPanel.
45026  * @param {Roo.grid.Grid} grid The grid for this panel
45027  * @param {String/Object} config A string to set only the panel's title, or a config object
45028  */
45029 Roo.GridPanel = function(grid, config){
45030     
45031   
45032     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45033         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45034         
45035     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45036     
45037     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45038     
45039     if(this.toolbar){
45040         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45041     }
45042     // xtype created footer. - not sure if will work as we normally have to render first..
45043     if (this.footer && !this.footer.el && this.footer.xtype) {
45044         
45045         this.footer.container = this.grid.getView().getFooterPanel(true);
45046         this.footer.dataSource = this.grid.dataSource;
45047         this.footer = Roo.factory(this.footer, Roo);
45048         
45049     }
45050     
45051     grid.monitorWindowResize = false; // turn off autosizing
45052     grid.autoHeight = false;
45053     grid.autoWidth = false;
45054     this.grid = grid;
45055     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45056 };
45057
45058 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45059     getId : function(){
45060         return this.grid.id;
45061     },
45062     
45063     /**
45064      * Returns the grid for this panel
45065      * @return {Roo.grid.Grid} 
45066      */
45067     getGrid : function(){
45068         return this.grid;    
45069     },
45070     
45071     setSize : function(width, height){
45072         if(!this.ignoreResize(width, height)){
45073             var grid = this.grid;
45074             var size = this.adjustForComponents(width, height);
45075             grid.getGridEl().setSize(size.width, size.height);
45076             grid.autoSize();
45077         }
45078     },
45079     
45080     beforeSlide : function(){
45081         this.grid.getView().scroller.clip();
45082     },
45083     
45084     afterSlide : function(){
45085         this.grid.getView().scroller.unclip();
45086     },
45087     
45088     destroy : function(){
45089         this.grid.destroy();
45090         delete this.grid;
45091         Roo.GridPanel.superclass.destroy.call(this); 
45092     }
45093 });
45094
45095
45096 /**
45097  * @class Roo.NestedLayoutPanel
45098  * @extends Roo.ContentPanel
45099  * @constructor
45100  * Create a new NestedLayoutPanel.
45101  * 
45102  * 
45103  * @param {Roo.BorderLayout} layout The layout for this panel
45104  * @param {String/Object} config A string to set only the title or a config object
45105  */
45106 Roo.NestedLayoutPanel = function(layout, config)
45107 {
45108     // construct with only one argument..
45109     /* FIXME - implement nicer consturctors
45110     if (layout.layout) {
45111         config = layout;
45112         layout = config.layout;
45113         delete config.layout;
45114     }
45115     if (layout.xtype && !layout.getEl) {
45116         // then layout needs constructing..
45117         layout = Roo.factory(layout, Roo);
45118     }
45119     */
45120     
45121     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45122     
45123     layout.monitorWindowResize = false; // turn off autosizing
45124     this.layout = layout;
45125     this.layout.getEl().addClass("x-layout-nested-layout");
45126     
45127     
45128     
45129 };
45130
45131 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45132
45133     setSize : function(width, height){
45134         if(!this.ignoreResize(width, height)){
45135             var size = this.adjustForComponents(width, height);
45136             var el = this.layout.getEl();
45137             el.setSize(size.width, size.height);
45138             var touch = el.dom.offsetWidth;
45139             this.layout.layout();
45140             // ie requires a double layout on the first pass
45141             if(Roo.isIE && !this.initialized){
45142                 this.initialized = true;
45143                 this.layout.layout();
45144             }
45145         }
45146     },
45147     
45148     // activate all subpanels if not currently active..
45149     
45150     setActiveState : function(active){
45151         this.active = active;
45152         if(!active){
45153             this.fireEvent("deactivate", this);
45154             return;
45155         }
45156         
45157         this.fireEvent("activate", this);
45158         // not sure if this should happen before or after..
45159         if (!this.layout) {
45160             return; // should not happen..
45161         }
45162         var reg = false;
45163         for (var r in this.layout.regions) {
45164             reg = this.layout.getRegion(r);
45165             if (reg.getActivePanel()) {
45166                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45167                 reg.setActivePanel(reg.getActivePanel());
45168                 continue;
45169             }
45170             if (!reg.panels.length) {
45171                 continue;
45172             }
45173             reg.showPanel(reg.getPanel(0));
45174         }
45175         
45176         
45177         
45178         
45179     },
45180     
45181     /**
45182      * Returns the nested BorderLayout for this panel
45183      * @return {Roo.BorderLayout} 
45184      */
45185     getLayout : function(){
45186         return this.layout;
45187     },
45188     
45189      /**
45190      * Adds a xtype elements to the layout of the nested panel
45191      * <pre><code>
45192
45193 panel.addxtype({
45194        xtype : 'ContentPanel',
45195        region: 'west',
45196        items: [ .... ]
45197    }
45198 );
45199
45200 panel.addxtype({
45201         xtype : 'NestedLayoutPanel',
45202         region: 'west',
45203         layout: {
45204            center: { },
45205            west: { }   
45206         },
45207         items : [ ... list of content panels or nested layout panels.. ]
45208    }
45209 );
45210 </code></pre>
45211      * @param {Object} cfg Xtype definition of item to add.
45212      */
45213     addxtype : function(cfg) {
45214         return this.layout.addxtype(cfg);
45215     
45216     }
45217 });
45218
45219 Roo.ScrollPanel = function(el, config, content){
45220     config = config || {};
45221     config.fitToFrame = true;
45222     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45223     
45224     this.el.dom.style.overflow = "hidden";
45225     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45226     this.el.removeClass("x-layout-inactive-content");
45227     this.el.on("mousewheel", this.onWheel, this);
45228
45229     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45230     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45231     up.unselectable(); down.unselectable();
45232     up.on("click", this.scrollUp, this);
45233     down.on("click", this.scrollDown, this);
45234     up.addClassOnOver("x-scroller-btn-over");
45235     down.addClassOnOver("x-scroller-btn-over");
45236     up.addClassOnClick("x-scroller-btn-click");
45237     down.addClassOnClick("x-scroller-btn-click");
45238     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45239
45240     this.resizeEl = this.el;
45241     this.el = wrap; this.up = up; this.down = down;
45242 };
45243
45244 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45245     increment : 100,
45246     wheelIncrement : 5,
45247     scrollUp : function(){
45248         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45249     },
45250
45251     scrollDown : function(){
45252         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45253     },
45254
45255     afterScroll : function(){
45256         var el = this.resizeEl;
45257         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45258         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45259         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45260     },
45261
45262     setSize : function(){
45263         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45264         this.afterScroll();
45265     },
45266
45267     onWheel : function(e){
45268         var d = e.getWheelDelta();
45269         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45270         this.afterScroll();
45271         e.stopEvent();
45272     },
45273
45274     setContent : function(content, loadScripts){
45275         this.resizeEl.update(content, loadScripts);
45276     }
45277
45278 });
45279
45280
45281
45282
45283
45284
45285
45286
45287
45288 /**
45289  * @class Roo.TreePanel
45290  * @extends Roo.ContentPanel
45291  * @constructor
45292  * Create a new TreePanel. - defaults to fit/scoll contents.
45293  * @param {String/Object} config A string to set only the panel's title, or a config object
45294  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45295  */
45296 Roo.TreePanel = function(config){
45297     var el = config.el;
45298     var tree = config.tree;
45299     delete config.tree; 
45300     delete config.el; // hopefull!
45301     
45302     // wrapper for IE7 strict & safari scroll issue
45303     
45304     var treeEl = el.createChild();
45305     config.resizeEl = treeEl;
45306     
45307     
45308     
45309     Roo.TreePanel.superclass.constructor.call(this, el, config);
45310  
45311  
45312     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45313     //console.log(tree);
45314     this.on('activate', function()
45315     {
45316         if (this.tree.rendered) {
45317             return;
45318         }
45319         //console.log('render tree');
45320         this.tree.render();
45321     });
45322     
45323     this.on('resize',  function (cp, w, h) {
45324             this.tree.innerCt.setWidth(w);
45325             this.tree.innerCt.setHeight(h);
45326             this.tree.innerCt.setStyle('overflow-y', 'auto');
45327     });
45328
45329         
45330     
45331 };
45332
45333 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45334     fitToFrame : true,
45335     autoScroll : true
45336 });
45337
45338
45339
45340
45341
45342
45343
45344
45345
45346
45347
45348 /*
45349  * Based on:
45350  * Ext JS Library 1.1.1
45351  * Copyright(c) 2006-2007, Ext JS, LLC.
45352  *
45353  * Originally Released Under LGPL - original licence link has changed is not relivant.
45354  *
45355  * Fork - LGPL
45356  * <script type="text/javascript">
45357  */
45358  
45359
45360 /**
45361  * @class Roo.ReaderLayout
45362  * @extends Roo.BorderLayout
45363  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45364  * center region containing two nested regions (a top one for a list view and one for item preview below),
45365  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45366  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45367  * expedites the setup of the overall layout and regions for this common application style.
45368  * Example:
45369  <pre><code>
45370 var reader = new Roo.ReaderLayout();
45371 var CP = Roo.ContentPanel;  // shortcut for adding
45372
45373 reader.beginUpdate();
45374 reader.add("north", new CP("north", "North"));
45375 reader.add("west", new CP("west", {title: "West"}));
45376 reader.add("east", new CP("east", {title: "East"}));
45377
45378 reader.regions.listView.add(new CP("listView", "List"));
45379 reader.regions.preview.add(new CP("preview", "Preview"));
45380 reader.endUpdate();
45381 </code></pre>
45382 * @constructor
45383 * Create a new ReaderLayout
45384 * @param {Object} config Configuration options
45385 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45386 * document.body if omitted)
45387 */
45388 Roo.ReaderLayout = function(config, renderTo){
45389     var c = config || {size:{}};
45390     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45391         north: c.north !== false ? Roo.apply({
45392             split:false,
45393             initialSize: 32,
45394             titlebar: false
45395         }, c.north) : false,
45396         west: c.west !== false ? Roo.apply({
45397             split:true,
45398             initialSize: 200,
45399             minSize: 175,
45400             maxSize: 400,
45401             titlebar: true,
45402             collapsible: true,
45403             animate: true,
45404             margins:{left:5,right:0,bottom:5,top:5},
45405             cmargins:{left:5,right:5,bottom:5,top:5}
45406         }, c.west) : false,
45407         east: c.east !== false ? Roo.apply({
45408             split:true,
45409             initialSize: 200,
45410             minSize: 175,
45411             maxSize: 400,
45412             titlebar: true,
45413             collapsible: true,
45414             animate: true,
45415             margins:{left:0,right:5,bottom:5,top:5},
45416             cmargins:{left:5,right:5,bottom:5,top:5}
45417         }, c.east) : false,
45418         center: Roo.apply({
45419             tabPosition: 'top',
45420             autoScroll:false,
45421             closeOnTab: true,
45422             titlebar:false,
45423             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45424         }, c.center)
45425     });
45426
45427     this.el.addClass('x-reader');
45428
45429     this.beginUpdate();
45430
45431     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45432         south: c.preview !== false ? Roo.apply({
45433             split:true,
45434             initialSize: 200,
45435             minSize: 100,
45436             autoScroll:true,
45437             collapsible:true,
45438             titlebar: true,
45439             cmargins:{top:5,left:0, right:0, bottom:0}
45440         }, c.preview) : false,
45441         center: Roo.apply({
45442             autoScroll:false,
45443             titlebar:false,
45444             minHeight:200
45445         }, c.listView)
45446     });
45447     this.add('center', new Roo.NestedLayoutPanel(inner,
45448             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45449
45450     this.endUpdate();
45451
45452     this.regions.preview = inner.getRegion('south');
45453     this.regions.listView = inner.getRegion('center');
45454 };
45455
45456 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45457  * Based on:
45458  * Ext JS Library 1.1.1
45459  * Copyright(c) 2006-2007, Ext JS, LLC.
45460  *
45461  * Originally Released Under LGPL - original licence link has changed is not relivant.
45462  *
45463  * Fork - LGPL
45464  * <script type="text/javascript">
45465  */
45466  
45467 /**
45468  * @class Roo.grid.Grid
45469  * @extends Roo.util.Observable
45470  * This class represents the primary interface of a component based grid control.
45471  * <br><br>Usage:<pre><code>
45472  var grid = new Roo.grid.Grid("my-container-id", {
45473      ds: myDataStore,
45474      cm: myColModel,
45475      selModel: mySelectionModel,
45476      autoSizeColumns: true,
45477      monitorWindowResize: false,
45478      trackMouseOver: true
45479  });
45480  // set any options
45481  grid.render();
45482  * </code></pre>
45483  * <b>Common Problems:</b><br/>
45484  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45485  * element will correct this<br/>
45486  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45487  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45488  * are unpredictable.<br/>
45489  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45490  * grid to calculate dimensions/offsets.<br/>
45491   * @constructor
45492  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45493  * The container MUST have some type of size defined for the grid to fill. The container will be
45494  * automatically set to position relative if it isn't already.
45495  * @param {Object} config A config object that sets properties on this grid.
45496  */
45497 Roo.grid.Grid = function(container, config){
45498         // initialize the container
45499         this.container = Roo.get(container);
45500         this.container.update("");
45501         this.container.setStyle("overflow", "hidden");
45502     this.container.addClass('x-grid-container');
45503
45504     this.id = this.container.id;
45505
45506     Roo.apply(this, config);
45507     // check and correct shorthanded configs
45508     if(this.ds){
45509         this.dataSource = this.ds;
45510         delete this.ds;
45511     }
45512     if(this.cm){
45513         this.colModel = this.cm;
45514         delete this.cm;
45515     }
45516     if(this.sm){
45517         this.selModel = this.sm;
45518         delete this.sm;
45519     }
45520
45521     if (this.selModel) {
45522         this.selModel = Roo.factory(this.selModel, Roo.grid);
45523         this.sm = this.selModel;
45524         this.sm.xmodule = this.xmodule || false;
45525     }
45526     if (typeof(this.colModel.config) == 'undefined') {
45527         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45528         this.cm = this.colModel;
45529         this.cm.xmodule = this.xmodule || false;
45530     }
45531     if (this.dataSource) {
45532         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45533         this.ds = this.dataSource;
45534         this.ds.xmodule = this.xmodule || false;
45535         
45536     }
45537     
45538     
45539     
45540     if(this.width){
45541         this.container.setWidth(this.width);
45542     }
45543
45544     if(this.height){
45545         this.container.setHeight(this.height);
45546     }
45547     /** @private */
45548         this.addEvents({
45549             // raw events
45550             /**
45551              * @event click
45552              * The raw click event for the entire grid.
45553              * @param {Roo.EventObject} e
45554              */
45555             "click" : true,
45556             /**
45557              * @event dblclick
45558              * The raw dblclick event for the entire grid.
45559              * @param {Roo.EventObject} e
45560              */
45561             "dblclick" : true,
45562             /**
45563              * @event contextmenu
45564              * The raw contextmenu event for the entire grid.
45565              * @param {Roo.EventObject} e
45566              */
45567             "contextmenu" : true,
45568             /**
45569              * @event mousedown
45570              * The raw mousedown event for the entire grid.
45571              * @param {Roo.EventObject} e
45572              */
45573             "mousedown" : true,
45574             /**
45575              * @event mouseup
45576              * The raw mouseup event for the entire grid.
45577              * @param {Roo.EventObject} e
45578              */
45579             "mouseup" : true,
45580             /**
45581              * @event mouseover
45582              * The raw mouseover event for the entire grid.
45583              * @param {Roo.EventObject} e
45584              */
45585             "mouseover" : true,
45586             /**
45587              * @event mouseout
45588              * The raw mouseout event for the entire grid.
45589              * @param {Roo.EventObject} e
45590              */
45591             "mouseout" : true,
45592             /**
45593              * @event keypress
45594              * The raw keypress event for the entire grid.
45595              * @param {Roo.EventObject} e
45596              */
45597             "keypress" : true,
45598             /**
45599              * @event keydown
45600              * The raw keydown event for the entire grid.
45601              * @param {Roo.EventObject} e
45602              */
45603             "keydown" : true,
45604
45605             // custom events
45606
45607             /**
45608              * @event cellclick
45609              * Fires when a cell is clicked
45610              * @param {Grid} this
45611              * @param {Number} rowIndex
45612              * @param {Number} columnIndex
45613              * @param {Roo.EventObject} e
45614              */
45615             "cellclick" : true,
45616             /**
45617              * @event celldblclick
45618              * Fires when a cell is double clicked
45619              * @param {Grid} this
45620              * @param {Number} rowIndex
45621              * @param {Number} columnIndex
45622              * @param {Roo.EventObject} e
45623              */
45624             "celldblclick" : true,
45625             /**
45626              * @event rowclick
45627              * Fires when a row is clicked
45628              * @param {Grid} this
45629              * @param {Number} rowIndex
45630              * @param {Roo.EventObject} e
45631              */
45632             "rowclick" : true,
45633             /**
45634              * @event rowdblclick
45635              * Fires when a row is double clicked
45636              * @param {Grid} this
45637              * @param {Number} rowIndex
45638              * @param {Roo.EventObject} e
45639              */
45640             "rowdblclick" : true,
45641             /**
45642              * @event headerclick
45643              * Fires when a header is clicked
45644              * @param {Grid} this
45645              * @param {Number} columnIndex
45646              * @param {Roo.EventObject} e
45647              */
45648             "headerclick" : true,
45649             /**
45650              * @event headerdblclick
45651              * Fires when a header cell is double clicked
45652              * @param {Grid} this
45653              * @param {Number} columnIndex
45654              * @param {Roo.EventObject} e
45655              */
45656             "headerdblclick" : true,
45657             /**
45658              * @event rowcontextmenu
45659              * Fires when a row is right clicked
45660              * @param {Grid} this
45661              * @param {Number} rowIndex
45662              * @param {Roo.EventObject} e
45663              */
45664             "rowcontextmenu" : true,
45665             /**
45666          * @event cellcontextmenu
45667          * Fires when a cell is right clicked
45668          * @param {Grid} this
45669          * @param {Number} rowIndex
45670          * @param {Number} cellIndex
45671          * @param {Roo.EventObject} e
45672          */
45673          "cellcontextmenu" : true,
45674             /**
45675              * @event headercontextmenu
45676              * Fires when a header is right clicked
45677              * @param {Grid} this
45678              * @param {Number} columnIndex
45679              * @param {Roo.EventObject} e
45680              */
45681             "headercontextmenu" : true,
45682             /**
45683              * @event bodyscroll
45684              * Fires when the body element is scrolled
45685              * @param {Number} scrollLeft
45686              * @param {Number} scrollTop
45687              */
45688             "bodyscroll" : true,
45689             /**
45690              * @event columnresize
45691              * Fires when the user resizes a column
45692              * @param {Number} columnIndex
45693              * @param {Number} newSize
45694              */
45695             "columnresize" : true,
45696             /**
45697              * @event columnmove
45698              * Fires when the user moves a column
45699              * @param {Number} oldIndex
45700              * @param {Number} newIndex
45701              */
45702             "columnmove" : true,
45703             /**
45704              * @event startdrag
45705              * Fires when row(s) start being dragged
45706              * @param {Grid} this
45707              * @param {Roo.GridDD} dd The drag drop object
45708              * @param {event} e The raw browser event
45709              */
45710             "startdrag" : true,
45711             /**
45712              * @event enddrag
45713              * Fires when a drag operation is complete
45714              * @param {Grid} this
45715              * @param {Roo.GridDD} dd The drag drop object
45716              * @param {event} e The raw browser event
45717              */
45718             "enddrag" : true,
45719             /**
45720              * @event dragdrop
45721              * Fires when dragged row(s) are dropped on a valid DD target
45722              * @param {Grid} this
45723              * @param {Roo.GridDD} dd The drag drop object
45724              * @param {String} targetId The target drag drop object
45725              * @param {event} e The raw browser event
45726              */
45727             "dragdrop" : true,
45728             /**
45729              * @event dragover
45730              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45731              * @param {Grid} this
45732              * @param {Roo.GridDD} dd The drag drop object
45733              * @param {String} targetId The target drag drop object
45734              * @param {event} e The raw browser event
45735              */
45736             "dragover" : true,
45737             /**
45738              * @event dragenter
45739              *  Fires when the dragged row(s) first cross another DD target while being dragged
45740              * @param {Grid} this
45741              * @param {Roo.GridDD} dd The drag drop object
45742              * @param {String} targetId The target drag drop object
45743              * @param {event} e The raw browser event
45744              */
45745             "dragenter" : true,
45746             /**
45747              * @event dragout
45748              * Fires when the dragged row(s) leave another DD target while being dragged
45749              * @param {Grid} this
45750              * @param {Roo.GridDD} dd The drag drop object
45751              * @param {String} targetId The target drag drop object
45752              * @param {event} e The raw browser event
45753              */
45754             "dragout" : true,
45755         /**
45756          * @event render
45757          * Fires when the grid is rendered
45758          * @param {Grid} grid
45759          */
45760         render : true
45761     });
45762
45763     Roo.grid.Grid.superclass.constructor.call(this);
45764 };
45765 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45766     
45767     /**
45768      * @cfg {String} ddGroup - drag drop group.
45769          */
45770     
45771     /**
45772      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45773          */
45774         minColumnWidth : 25,
45775
45776     /**
45777          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45778          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45779          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45780          */
45781         autoSizeColumns : false,
45782
45783         /**
45784          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45785          */
45786         autoSizeHeaders : true,
45787
45788         /**
45789          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45790          */
45791         monitorWindowResize : true,
45792
45793         /**
45794          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45795          * rows measured to get a columns size. Default is 0 (all rows).
45796          */
45797         maxRowsToMeasure : 0,
45798
45799         /**
45800          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45801          */
45802         trackMouseOver : true,
45803
45804     /**
45805          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
45806          */
45807     
45808         /**
45809          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45810          */
45811         enableDragDrop : false,
45812
45813         /**
45814          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45815          */
45816         enableColumnMove : true,
45817
45818         /**
45819          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45820          */
45821         enableColumnHide : true,
45822
45823         /**
45824          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45825          */
45826         enableRowHeightSync : false,
45827
45828         /**
45829          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45830          */
45831         stripeRows : true,
45832
45833         /**
45834          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45835          */
45836         autoHeight : false,
45837
45838     /**
45839      * @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.
45840      */
45841     autoExpandColumn : false,
45842
45843     /**
45844     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45845     * Default is 50.
45846     */
45847     autoExpandMin : 50,
45848
45849     /**
45850     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45851     */
45852     autoExpandMax : 1000,
45853
45854     /**
45855          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45856          */
45857         view : null,
45858
45859         /**
45860      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45861          */
45862         loadMask : false,
45863
45864     // private
45865     rendered : false,
45866
45867     /**
45868     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45869     * of a fixed width. Default is false.
45870     */
45871     /**
45872     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45873     */
45874     /**
45875      * Called once after all setup has been completed and the grid is ready to be rendered.
45876      * @return {Roo.grid.Grid} this
45877      */
45878     render : function(){
45879         var c = this.container;
45880         // try to detect autoHeight/width mode
45881         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45882             this.autoHeight = true;
45883         }
45884         var view = this.getView();
45885         view.init(this);
45886
45887         c.on("click", this.onClick, this);
45888         c.on("dblclick", this.onDblClick, this);
45889         c.on("contextmenu", this.onContextMenu, this);
45890         c.on("keydown", this.onKeyDown, this);
45891
45892         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45893
45894         this.getSelectionModel().init(this);
45895
45896         view.render();
45897
45898         if(this.loadMask){
45899             this.loadMask = new Roo.LoadMask(this.container,
45900                     Roo.apply({store:this.dataSource}, this.loadMask));
45901         }
45902         
45903         
45904         if (this.toolbar && this.toolbar.xtype) {
45905             this.toolbar.container = this.getView().getHeaderPanel(true);
45906             this.toolbar = new Ext.Toolbar(this.toolbar);
45907         }
45908         if (this.footer && this.footer.xtype) {
45909             this.footer.dataSource = this.getDataSource();
45910             this.footer.container = this.getView().getFooterPanel(true);
45911             this.footer = Roo.factory(this.footer, Roo);
45912         }
45913         this.rendered = true;
45914         this.fireEvent('render', this);
45915         return this;
45916     },
45917
45918         /**
45919          * Reconfigures the grid to use a different Store and Column Model.
45920          * The View will be bound to the new objects and refreshed.
45921          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45922          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45923          */
45924     reconfigure : function(dataSource, colModel){
45925         if(this.loadMask){
45926             this.loadMask.destroy();
45927             this.loadMask = new Roo.LoadMask(this.container,
45928                     Roo.apply({store:dataSource}, this.loadMask));
45929         }
45930         this.view.bind(dataSource, colModel);
45931         this.dataSource = dataSource;
45932         this.colModel = colModel;
45933         this.view.refresh(true);
45934     },
45935
45936     // private
45937     onKeyDown : function(e){
45938         this.fireEvent("keydown", e);
45939     },
45940
45941     /**
45942      * Destroy this grid.
45943      * @param {Boolean} removeEl True to remove the element
45944      */
45945     destroy : function(removeEl, keepListeners){
45946         if(this.loadMask){
45947             this.loadMask.destroy();
45948         }
45949         var c = this.container;
45950         c.removeAllListeners();
45951         this.view.destroy();
45952         this.colModel.purgeListeners();
45953         if(!keepListeners){
45954             this.purgeListeners();
45955         }
45956         c.update("");
45957         if(removeEl === true){
45958             c.remove();
45959         }
45960     },
45961
45962     // private
45963     processEvent : function(name, e){
45964         this.fireEvent(name, e);
45965         var t = e.getTarget();
45966         var v = this.view;
45967         var header = v.findHeaderIndex(t);
45968         if(header !== false){
45969             this.fireEvent("header" + name, this, header, e);
45970         }else{
45971             var row = v.findRowIndex(t);
45972             var cell = v.findCellIndex(t);
45973             if(row !== false){
45974                 this.fireEvent("row" + name, this, row, e);
45975                 if(cell !== false){
45976                     this.fireEvent("cell" + name, this, row, cell, e);
45977                 }
45978             }
45979         }
45980     },
45981
45982     // private
45983     onClick : function(e){
45984         this.processEvent("click", e);
45985     },
45986
45987     // private
45988     onContextMenu : function(e, t){
45989         this.processEvent("contextmenu", e);
45990     },
45991
45992     // private
45993     onDblClick : function(e){
45994         this.processEvent("dblclick", e);
45995     },
45996
45997     // private
45998     walkCells : function(row, col, step, fn, scope){
45999         var cm = this.colModel, clen = cm.getColumnCount();
46000         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46001         if(step < 0){
46002             if(col < 0){
46003                 row--;
46004                 first = false;
46005             }
46006             while(row >= 0){
46007                 if(!first){
46008                     col = clen-1;
46009                 }
46010                 first = false;
46011                 while(col >= 0){
46012                     if(fn.call(scope || this, row, col, cm) === true){
46013                         return [row, col];
46014                     }
46015                     col--;
46016                 }
46017                 row--;
46018             }
46019         } else {
46020             if(col >= clen){
46021                 row++;
46022                 first = false;
46023             }
46024             while(row < rlen){
46025                 if(!first){
46026                     col = 0;
46027                 }
46028                 first = false;
46029                 while(col < clen){
46030                     if(fn.call(scope || this, row, col, cm) === true){
46031                         return [row, col];
46032                     }
46033                     col++;
46034                 }
46035                 row++;
46036             }
46037         }
46038         return null;
46039     },
46040
46041     // private
46042     getSelections : function(){
46043         return this.selModel.getSelections();
46044     },
46045
46046     /**
46047      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46048      * but if manual update is required this method will initiate it.
46049      */
46050     autoSize : function(){
46051         if(this.rendered){
46052             this.view.layout();
46053             if(this.view.adjustForScroll){
46054                 this.view.adjustForScroll();
46055             }
46056         }
46057     },
46058
46059     /**
46060      * Returns the grid's underlying element.
46061      * @return {Element} The element
46062      */
46063     getGridEl : function(){
46064         return this.container;
46065     },
46066
46067     // private for compatibility, overridden by editor grid
46068     stopEditing : function(){},
46069
46070     /**
46071      * Returns the grid's SelectionModel.
46072      * @return {SelectionModel}
46073      */
46074     getSelectionModel : function(){
46075         if(!this.selModel){
46076             this.selModel = new Roo.grid.RowSelectionModel();
46077         }
46078         return this.selModel;
46079     },
46080
46081     /**
46082      * Returns the grid's DataSource.
46083      * @return {DataSource}
46084      */
46085     getDataSource : function(){
46086         return this.dataSource;
46087     },
46088
46089     /**
46090      * Returns the grid's ColumnModel.
46091      * @return {ColumnModel}
46092      */
46093     getColumnModel : function(){
46094         return this.colModel;
46095     },
46096
46097     /**
46098      * Returns the grid's GridView object.
46099      * @return {GridView}
46100      */
46101     getView : function(){
46102         if(!this.view){
46103             this.view = new Roo.grid.GridView(this.viewConfig);
46104         }
46105         return this.view;
46106     },
46107     /**
46108      * Called to get grid's drag proxy text, by default returns this.ddText.
46109      * @return {String}
46110      */
46111     getDragDropText : function(){
46112         var count = this.selModel.getCount();
46113         return String.format(this.ddText, count, count == 1 ? '' : 's');
46114     }
46115 });
46116 /**
46117  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46118  * %0 is replaced with the number of selected rows.
46119  * @type String
46120  */
46121 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46122  * Based on:
46123  * Ext JS Library 1.1.1
46124  * Copyright(c) 2006-2007, Ext JS, LLC.
46125  *
46126  * Originally Released Under LGPL - original licence link has changed is not relivant.
46127  *
46128  * Fork - LGPL
46129  * <script type="text/javascript">
46130  */
46131  
46132 Roo.grid.AbstractGridView = function(){
46133         this.grid = null;
46134         
46135         this.events = {
46136             "beforerowremoved" : true,
46137             "beforerowsinserted" : true,
46138             "beforerefresh" : true,
46139             "rowremoved" : true,
46140             "rowsinserted" : true,
46141             "rowupdated" : true,
46142             "refresh" : true
46143         };
46144     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46145 };
46146
46147 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46148     rowClass : "x-grid-row",
46149     cellClass : "x-grid-cell",
46150     tdClass : "x-grid-td",
46151     hdClass : "x-grid-hd",
46152     splitClass : "x-grid-hd-split",
46153     
46154         init: function(grid){
46155         this.grid = grid;
46156                 var cid = this.grid.getGridEl().id;
46157         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46158         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46159         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46160         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46161         },
46162         
46163         getColumnRenderers : function(){
46164         var renderers = [];
46165         var cm = this.grid.colModel;
46166         var colCount = cm.getColumnCount();
46167         for(var i = 0; i < colCount; i++){
46168             renderers[i] = cm.getRenderer(i);
46169         }
46170         return renderers;
46171     },
46172     
46173     getColumnIds : function(){
46174         var ids = [];
46175         var cm = this.grid.colModel;
46176         var colCount = cm.getColumnCount();
46177         for(var i = 0; i < colCount; i++){
46178             ids[i] = cm.getColumnId(i);
46179         }
46180         return ids;
46181     },
46182     
46183     getDataIndexes : function(){
46184         if(!this.indexMap){
46185             this.indexMap = this.buildIndexMap();
46186         }
46187         return this.indexMap.colToData;
46188     },
46189     
46190     getColumnIndexByDataIndex : function(dataIndex){
46191         if(!this.indexMap){
46192             this.indexMap = this.buildIndexMap();
46193         }
46194         return this.indexMap.dataToCol[dataIndex];
46195     },
46196     
46197     /**
46198      * Set a css style for a column dynamically. 
46199      * @param {Number} colIndex The index of the column
46200      * @param {String} name The css property name
46201      * @param {String} value The css value
46202      */
46203     setCSSStyle : function(colIndex, name, value){
46204         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46205         Roo.util.CSS.updateRule(selector, name, value);
46206     },
46207     
46208     generateRules : function(cm){
46209         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46210         Roo.util.CSS.removeStyleSheet(rulesId);
46211         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46212             var cid = cm.getColumnId(i);
46213             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46214                          this.tdSelector, cid, " {\n}\n",
46215                          this.hdSelector, cid, " {\n}\n",
46216                          this.splitSelector, cid, " {\n}\n");
46217         }
46218         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46219     }
46220 });/*
46221  * Based on:
46222  * Ext JS Library 1.1.1
46223  * Copyright(c) 2006-2007, Ext JS, LLC.
46224  *
46225  * Originally Released Under LGPL - original licence link has changed is not relivant.
46226  *
46227  * Fork - LGPL
46228  * <script type="text/javascript">
46229  */
46230
46231 // private
46232 // This is a support class used internally by the Grid components
46233 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46234     this.grid = grid;
46235     this.view = grid.getView();
46236     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46237     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46238     if(hd2){
46239         this.setHandleElId(Roo.id(hd));
46240         this.setOuterHandleElId(Roo.id(hd2));
46241     }
46242     this.scroll = false;
46243 };
46244 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46245     maxDragWidth: 120,
46246     getDragData : function(e){
46247         var t = Roo.lib.Event.getTarget(e);
46248         var h = this.view.findHeaderCell(t);
46249         if(h){
46250             return {ddel: h.firstChild, header:h};
46251         }
46252         return false;
46253     },
46254
46255     onInitDrag : function(e){
46256         this.view.headersDisabled = true;
46257         var clone = this.dragData.ddel.cloneNode(true);
46258         clone.id = Roo.id();
46259         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46260         this.proxy.update(clone);
46261         return true;
46262     },
46263
46264     afterValidDrop : function(){
46265         var v = this.view;
46266         setTimeout(function(){
46267             v.headersDisabled = false;
46268         }, 50);
46269     },
46270
46271     afterInvalidDrop : function(){
46272         var v = this.view;
46273         setTimeout(function(){
46274             v.headersDisabled = false;
46275         }, 50);
46276     }
46277 });
46278 /*
46279  * Based on:
46280  * Ext JS Library 1.1.1
46281  * Copyright(c) 2006-2007, Ext JS, LLC.
46282  *
46283  * Originally Released Under LGPL - original licence link has changed is not relivant.
46284  *
46285  * Fork - LGPL
46286  * <script type="text/javascript">
46287  */
46288 // private
46289 // This is a support class used internally by the Grid components
46290 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46291     this.grid = grid;
46292     this.view = grid.getView();
46293     // split the proxies so they don't interfere with mouse events
46294     this.proxyTop = Roo.DomHelper.append(document.body, {
46295         cls:"col-move-top", html:"&#160;"
46296     }, true);
46297     this.proxyBottom = Roo.DomHelper.append(document.body, {
46298         cls:"col-move-bottom", html:"&#160;"
46299     }, true);
46300     this.proxyTop.hide = this.proxyBottom.hide = function(){
46301         this.setLeftTop(-100,-100);
46302         this.setStyle("visibility", "hidden");
46303     };
46304     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46305     // temporarily disabled
46306     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46307     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46308 };
46309 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46310     proxyOffsets : [-4, -9],
46311     fly: Roo.Element.fly,
46312
46313     getTargetFromEvent : function(e){
46314         var t = Roo.lib.Event.getTarget(e);
46315         var cindex = this.view.findCellIndex(t);
46316         if(cindex !== false){
46317             return this.view.getHeaderCell(cindex);
46318         }
46319     },
46320
46321     nextVisible : function(h){
46322         var v = this.view, cm = this.grid.colModel;
46323         h = h.nextSibling;
46324         while(h){
46325             if(!cm.isHidden(v.getCellIndex(h))){
46326                 return h;
46327             }
46328             h = h.nextSibling;
46329         }
46330         return null;
46331     },
46332
46333     prevVisible : function(h){
46334         var v = this.view, cm = this.grid.colModel;
46335         h = h.prevSibling;
46336         while(h){
46337             if(!cm.isHidden(v.getCellIndex(h))){
46338                 return h;
46339             }
46340             h = h.prevSibling;
46341         }
46342         return null;
46343     },
46344
46345     positionIndicator : function(h, n, e){
46346         var x = Roo.lib.Event.getPageX(e);
46347         var r = Roo.lib.Dom.getRegion(n.firstChild);
46348         var px, pt, py = r.top + this.proxyOffsets[1];
46349         if((r.right - x) <= (r.right-r.left)/2){
46350             px = r.right+this.view.borderWidth;
46351             pt = "after";
46352         }else{
46353             px = r.left;
46354             pt = "before";
46355         }
46356         var oldIndex = this.view.getCellIndex(h);
46357         var newIndex = this.view.getCellIndex(n);
46358
46359         if(this.grid.colModel.isFixed(newIndex)){
46360             return false;
46361         }
46362
46363         var locked = this.grid.colModel.isLocked(newIndex);
46364
46365         if(pt == "after"){
46366             newIndex++;
46367         }
46368         if(oldIndex < newIndex){
46369             newIndex--;
46370         }
46371         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46372             return false;
46373         }
46374         px +=  this.proxyOffsets[0];
46375         this.proxyTop.setLeftTop(px, py);
46376         this.proxyTop.show();
46377         if(!this.bottomOffset){
46378             this.bottomOffset = this.view.mainHd.getHeight();
46379         }
46380         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46381         this.proxyBottom.show();
46382         return pt;
46383     },
46384
46385     onNodeEnter : function(n, dd, e, data){
46386         if(data.header != n){
46387             this.positionIndicator(data.header, n, e);
46388         }
46389     },
46390
46391     onNodeOver : function(n, dd, e, data){
46392         var result = false;
46393         if(data.header != n){
46394             result = this.positionIndicator(data.header, n, e);
46395         }
46396         if(!result){
46397             this.proxyTop.hide();
46398             this.proxyBottom.hide();
46399         }
46400         return result ? this.dropAllowed : this.dropNotAllowed;
46401     },
46402
46403     onNodeOut : function(n, dd, e, data){
46404         this.proxyTop.hide();
46405         this.proxyBottom.hide();
46406     },
46407
46408     onNodeDrop : function(n, dd, e, data){
46409         var h = data.header;
46410         if(h != n){
46411             var cm = this.grid.colModel;
46412             var x = Roo.lib.Event.getPageX(e);
46413             var r = Roo.lib.Dom.getRegion(n.firstChild);
46414             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46415             var oldIndex = this.view.getCellIndex(h);
46416             var newIndex = this.view.getCellIndex(n);
46417             var locked = cm.isLocked(newIndex);
46418             if(pt == "after"){
46419                 newIndex++;
46420             }
46421             if(oldIndex < newIndex){
46422                 newIndex--;
46423             }
46424             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46425                 return false;
46426             }
46427             cm.setLocked(oldIndex, locked, true);
46428             cm.moveColumn(oldIndex, newIndex);
46429             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46430             return true;
46431         }
46432         return false;
46433     }
46434 });
46435 /*
46436  * Based on:
46437  * Ext JS Library 1.1.1
46438  * Copyright(c) 2006-2007, Ext JS, LLC.
46439  *
46440  * Originally Released Under LGPL - original licence link has changed is not relivant.
46441  *
46442  * Fork - LGPL
46443  * <script type="text/javascript">
46444  */
46445   
46446 /**
46447  * @class Roo.grid.GridView
46448  * @extends Roo.util.Observable
46449  *
46450  * @constructor
46451  * @param {Object} config
46452  */
46453 Roo.grid.GridView = function(config){
46454     Roo.grid.GridView.superclass.constructor.call(this);
46455     this.el = null;
46456
46457     Roo.apply(this, config);
46458 };
46459
46460 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46461
46462     /**
46463      * Override this function to apply custom css classes to rows during rendering
46464      * @param {Record} record The record
46465      * @param {Number} index
46466      * @method getRowClass
46467      */
46468     rowClass : "x-grid-row",
46469
46470     cellClass : "x-grid-col",
46471
46472     tdClass : "x-grid-td",
46473
46474     hdClass : "x-grid-hd",
46475
46476     splitClass : "x-grid-split",
46477
46478     sortClasses : ["sort-asc", "sort-desc"],
46479
46480     enableMoveAnim : false,
46481
46482     hlColor: "C3DAF9",
46483
46484     dh : Roo.DomHelper,
46485
46486     fly : Roo.Element.fly,
46487
46488     css : Roo.util.CSS,
46489
46490     borderWidth: 1,
46491
46492     splitOffset: 3,
46493
46494     scrollIncrement : 22,
46495
46496     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46497
46498     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46499
46500     bind : function(ds, cm){
46501         if(this.ds){
46502             this.ds.un("load", this.onLoad, this);
46503             this.ds.un("datachanged", this.onDataChange, this);
46504             this.ds.un("add", this.onAdd, this);
46505             this.ds.un("remove", this.onRemove, this);
46506             this.ds.un("update", this.onUpdate, this);
46507             this.ds.un("clear", this.onClear, this);
46508         }
46509         if(ds){
46510             ds.on("load", this.onLoad, this);
46511             ds.on("datachanged", this.onDataChange, this);
46512             ds.on("add", this.onAdd, this);
46513             ds.on("remove", this.onRemove, this);
46514             ds.on("update", this.onUpdate, this);
46515             ds.on("clear", this.onClear, this);
46516         }
46517         this.ds = ds;
46518
46519         if(this.cm){
46520             this.cm.un("widthchange", this.onColWidthChange, this);
46521             this.cm.un("headerchange", this.onHeaderChange, this);
46522             this.cm.un("hiddenchange", this.onHiddenChange, this);
46523             this.cm.un("columnmoved", this.onColumnMove, this);
46524             this.cm.un("columnlockchange", this.onColumnLock, this);
46525         }
46526         if(cm){
46527             this.generateRules(cm);
46528             cm.on("widthchange", this.onColWidthChange, this);
46529             cm.on("headerchange", this.onHeaderChange, this);
46530             cm.on("hiddenchange", this.onHiddenChange, this);
46531             cm.on("columnmoved", this.onColumnMove, this);
46532             cm.on("columnlockchange", this.onColumnLock, this);
46533         }
46534         this.cm = cm;
46535     },
46536
46537     init: function(grid){
46538                 Roo.grid.GridView.superclass.init.call(this, grid);
46539
46540                 this.bind(grid.dataSource, grid.colModel);
46541
46542             grid.on("headerclick", this.handleHeaderClick, this);
46543
46544         if(grid.trackMouseOver){
46545             grid.on("mouseover", this.onRowOver, this);
46546                 grid.on("mouseout", this.onRowOut, this);
46547             }
46548             grid.cancelTextSelection = function(){};
46549                 this.gridId = grid.id;
46550
46551                 var tpls = this.templates || {};
46552
46553                 if(!tpls.master){
46554                     tpls.master = new Roo.Template(
46555                        '<div class="x-grid" hidefocus="true">',
46556                           '<div class="x-grid-topbar"></div>',
46557                           '<div class="x-grid-scroller"><div></div></div>',
46558                           '<div class="x-grid-locked">',
46559                               '<div class="x-grid-header">{lockedHeader}</div>',
46560                               '<div class="x-grid-body">{lockedBody}</div>',
46561                           "</div>",
46562                           '<div class="x-grid-viewport">',
46563                               '<div class="x-grid-header">{header}</div>',
46564                               '<div class="x-grid-body">{body}</div>',
46565                           "</div>",
46566                           '<div class="x-grid-bottombar"></div>',
46567                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46568                           '<div class="x-grid-resize-proxy">&#160;</div>',
46569                        "</div>"
46570                     );
46571                     tpls.master.disableformats = true;
46572                 }
46573
46574                 if(!tpls.header){
46575                     tpls.header = new Roo.Template(
46576                        '<table border="0" cellspacing="0" cellpadding="0">',
46577                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46578                        "</table>{splits}"
46579                     );
46580                     tpls.header.disableformats = true;
46581                 }
46582                 tpls.header.compile();
46583
46584                 if(!tpls.hcell){
46585                     tpls.hcell = new Roo.Template(
46586                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46587                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46588                         "</div></td>"
46589                      );
46590                      tpls.hcell.disableFormats = true;
46591                 }
46592                 tpls.hcell.compile();
46593
46594                 if(!tpls.hsplit){
46595                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46596                     tpls.hsplit.disableFormats = true;
46597                 }
46598                 tpls.hsplit.compile();
46599
46600                 if(!tpls.body){
46601                     tpls.body = new Roo.Template(
46602                        '<table border="0" cellspacing="0" cellpadding="0">',
46603                        "<tbody>{rows}</tbody>",
46604                        "</table>"
46605                     );
46606                     tpls.body.disableFormats = true;
46607                 }
46608                 tpls.body.compile();
46609
46610                 if(!tpls.row){
46611                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46612                     tpls.row.disableFormats = true;
46613                 }
46614                 tpls.row.compile();
46615
46616                 if(!tpls.cell){
46617                     tpls.cell = new Roo.Template(
46618                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46619                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46620                         "</td>"
46621                     );
46622             tpls.cell.disableFormats = true;
46623         }
46624                 tpls.cell.compile();
46625
46626                 this.templates = tpls;
46627         },
46628
46629         // remap these for backwards compat
46630     onColWidthChange : function(){
46631         this.updateColumns.apply(this, arguments);
46632     },
46633     onHeaderChange : function(){
46634         this.updateHeaders.apply(this, arguments);
46635     }, 
46636     onHiddenChange : function(){
46637         this.handleHiddenChange.apply(this, arguments);
46638     },
46639     onColumnMove : function(){
46640         this.handleColumnMove.apply(this, arguments);
46641     },
46642     onColumnLock : function(){
46643         this.handleLockChange.apply(this, arguments);
46644     },
46645
46646     onDataChange : function(){
46647         this.refresh();
46648         this.updateHeaderSortState();
46649     },
46650
46651         onClear : function(){
46652         this.refresh();
46653     },
46654
46655         onUpdate : function(ds, record){
46656         this.refreshRow(record);
46657     },
46658
46659     refreshRow : function(record){
46660         var ds = this.ds, index;
46661         if(typeof record == 'number'){
46662             index = record;
46663             record = ds.getAt(index);
46664         }else{
46665             index = ds.indexOf(record);
46666         }
46667         this.insertRows(ds, index, index, true);
46668         this.onRemove(ds, record, index+1, true);
46669         this.syncRowHeights(index, index);
46670         this.layout();
46671         this.fireEvent("rowupdated", this, index, record);
46672     },
46673
46674     onAdd : function(ds, records, index){
46675         this.insertRows(ds, index, index + (records.length-1));
46676     },
46677
46678     onRemove : function(ds, record, index, isUpdate){
46679         if(isUpdate !== true){
46680             this.fireEvent("beforerowremoved", this, index, record);
46681         }
46682         var bt = this.getBodyTable(), lt = this.getLockedTable();
46683         if(bt.rows[index]){
46684             bt.firstChild.removeChild(bt.rows[index]);
46685         }
46686         if(lt.rows[index]){
46687             lt.firstChild.removeChild(lt.rows[index]);
46688         }
46689         if(isUpdate !== true){
46690             this.stripeRows(index);
46691             this.syncRowHeights(index, index);
46692             this.layout();
46693             this.fireEvent("rowremoved", this, index, record);
46694         }
46695     },
46696
46697     onLoad : function(){
46698         this.scrollToTop();
46699     },
46700
46701     /**
46702      * Scrolls the grid to the top
46703      */
46704     scrollToTop : function(){
46705         if(this.scroller){
46706             this.scroller.dom.scrollTop = 0;
46707             this.syncScroll();
46708         }
46709     },
46710
46711     /**
46712      * Gets a panel in the header of the grid that can be used for toolbars etc.
46713      * After modifying the contents of this panel a call to grid.autoSize() may be
46714      * required to register any changes in size.
46715      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46716      * @return Roo.Element
46717      */
46718     getHeaderPanel : function(doShow){
46719         if(doShow){
46720             this.headerPanel.show();
46721         }
46722         return this.headerPanel;
46723         },
46724
46725         /**
46726      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46727      * After modifying the contents of this panel a call to grid.autoSize() may be
46728      * required to register any changes in size.
46729      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46730      * @return Roo.Element
46731      */
46732     getFooterPanel : function(doShow){
46733         if(doShow){
46734             this.footerPanel.show();
46735         }
46736         return this.footerPanel;
46737         },
46738
46739         initElements : function(){
46740             var E = Roo.Element;
46741             var el = this.grid.getGridEl().dom.firstChild;
46742             var cs = el.childNodes;
46743
46744             this.el = new E(el);
46745             this.headerPanel = new E(el.firstChild);
46746             this.headerPanel.enableDisplayMode("block");
46747
46748         this.scroller = new E(cs[1]);
46749             this.scrollSizer = new E(this.scroller.dom.firstChild);
46750
46751             this.lockedWrap = new E(cs[2]);
46752             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46753             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46754
46755             this.mainWrap = new E(cs[3]);
46756             this.mainHd = new E(this.mainWrap.dom.firstChild);
46757             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46758
46759             this.footerPanel = new E(cs[4]);
46760             this.footerPanel.enableDisplayMode("block");
46761
46762         this.focusEl = new E(cs[5]);
46763         this.focusEl.swallowEvent("click", true);
46764         this.resizeProxy = new E(cs[6]);
46765
46766             this.headerSelector = String.format(
46767                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46768                this.lockedHd.id, this.mainHd.id
46769             );
46770
46771             this.splitterSelector = String.format(
46772                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46773                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46774             );
46775     },
46776     idToCssName : function(s)
46777     {
46778         return s.replace(/[^a-z0-9]+/ig, '-');
46779     },
46780
46781         getHeaderCell : function(index){
46782             return Roo.DomQuery.select(this.headerSelector)[index];
46783         },
46784
46785         getHeaderCellMeasure : function(index){
46786             return this.getHeaderCell(index).firstChild;
46787         },
46788
46789         getHeaderCellText : function(index){
46790             return this.getHeaderCell(index).firstChild.firstChild;
46791         },
46792
46793         getLockedTable : function(){
46794             return this.lockedBody.dom.firstChild;
46795         },
46796
46797         getBodyTable : function(){
46798             return this.mainBody.dom.firstChild;
46799         },
46800
46801         getLockedRow : function(index){
46802             return this.getLockedTable().rows[index];
46803         },
46804
46805         getRow : function(index){
46806             return this.getBodyTable().rows[index];
46807         },
46808
46809         getRowComposite : function(index){
46810             if(!this.rowEl){
46811                 this.rowEl = new Roo.CompositeElementLite();
46812             }
46813         var els = [], lrow, mrow;
46814         if(lrow = this.getLockedRow(index)){
46815             els.push(lrow);
46816         }
46817         if(mrow = this.getRow(index)){
46818             els.push(mrow);
46819         }
46820         this.rowEl.elements = els;
46821             return this.rowEl;
46822         },
46823
46824         getCell : function(rowIndex, colIndex){
46825             var locked = this.cm.getLockedCount();
46826             var source;
46827             if(colIndex < locked){
46828                 source = this.lockedBody.dom.firstChild;
46829             }else{
46830                 source = this.mainBody.dom.firstChild;
46831                 colIndex -= locked;
46832             }
46833         return source.rows[rowIndex].childNodes[colIndex];
46834         },
46835
46836         getCellText : function(rowIndex, colIndex){
46837             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46838         },
46839
46840         getCellBox : function(cell){
46841             var b = this.fly(cell).getBox();
46842         if(Roo.isOpera){ // opera fails to report the Y
46843             b.y = cell.offsetTop + this.mainBody.getY();
46844         }
46845         return b;
46846     },
46847
46848     getCellIndex : function(cell){
46849         var id = String(cell.className).match(this.cellRE);
46850         if(id){
46851             return parseInt(id[1], 10);
46852         }
46853         return 0;
46854     },
46855
46856     findHeaderIndex : function(n){
46857         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46858         return r ? this.getCellIndex(r) : false;
46859     },
46860
46861     findHeaderCell : function(n){
46862         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46863         return r ? r : false;
46864     },
46865
46866     findRowIndex : function(n){
46867         if(!n){
46868             return false;
46869         }
46870         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46871         return r ? r.rowIndex : false;
46872     },
46873
46874     findCellIndex : function(node){
46875         var stop = this.el.dom;
46876         while(node && node != stop){
46877             if(this.findRE.test(node.className)){
46878                 return this.getCellIndex(node);
46879             }
46880             node = node.parentNode;
46881         }
46882         return false;
46883     },
46884
46885     getColumnId : function(index){
46886             return this.cm.getColumnId(index);
46887         },
46888
46889         getSplitters : function(){
46890             if(this.splitterSelector){
46891                return Roo.DomQuery.select(this.splitterSelector);
46892             }else{
46893                 return null;
46894             }
46895         },
46896
46897         getSplitter : function(index){
46898             return this.getSplitters()[index];
46899         },
46900
46901     onRowOver : function(e, t){
46902         var row;
46903         if((row = this.findRowIndex(t)) !== false){
46904             this.getRowComposite(row).addClass("x-grid-row-over");
46905         }
46906     },
46907
46908     onRowOut : function(e, t){
46909         var row;
46910         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46911             this.getRowComposite(row).removeClass("x-grid-row-over");
46912         }
46913     },
46914
46915     renderHeaders : function(){
46916             var cm = this.cm;
46917         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46918         var cb = [], lb = [], sb = [], lsb = [], p = {};
46919         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46920             p.cellId = "x-grid-hd-0-" + i;
46921             p.splitId = "x-grid-csplit-0-" + i;
46922             p.id = cm.getColumnId(i);
46923             p.title = cm.getColumnTooltip(i) || "";
46924             p.value = cm.getColumnHeader(i) || "";
46925             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46926             if(!cm.isLocked(i)){
46927                 cb[cb.length] = ct.apply(p);
46928                 sb[sb.length] = st.apply(p);
46929             }else{
46930                 lb[lb.length] = ct.apply(p);
46931                 lsb[lsb.length] = st.apply(p);
46932             }
46933         }
46934         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46935                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46936         },
46937
46938         updateHeaders : function(){
46939         var html = this.renderHeaders();
46940         this.lockedHd.update(html[0]);
46941         this.mainHd.update(html[1]);
46942     },
46943
46944     /**
46945      * Focuses the specified row.
46946      * @param {Number} row The row index
46947      */
46948     focusRow : function(row){
46949         var x = this.scroller.dom.scrollLeft;
46950         this.focusCell(row, 0, false);
46951         this.scroller.dom.scrollLeft = x;
46952     },
46953
46954     /**
46955      * Focuses the specified cell.
46956      * @param {Number} row The row index
46957      * @param {Number} col The column index
46958      * @param {Boolean} hscroll false to disable horizontal scrolling
46959      */
46960     focusCell : function(row, col, hscroll){
46961         var el = this.ensureVisible(row, col, hscroll);
46962         this.focusEl.alignTo(el, "tl-tl");
46963         if(Roo.isGecko){
46964             this.focusEl.focus();
46965         }else{
46966             this.focusEl.focus.defer(1, this.focusEl);
46967         }
46968     },
46969
46970     /**
46971      * Scrolls the specified cell into view
46972      * @param {Number} row The row index
46973      * @param {Number} col The column index
46974      * @param {Boolean} hscroll false to disable horizontal scrolling
46975      */
46976     ensureVisible : function(row, col, hscroll){
46977         if(typeof row != "number"){
46978             row = row.rowIndex;
46979         }
46980         if(row < 0 && row >= this.ds.getCount()){
46981             return;
46982         }
46983         col = (col !== undefined ? col : 0);
46984         var cm = this.grid.colModel;
46985         while(cm.isHidden(col)){
46986             col++;
46987         }
46988
46989         var el = this.getCell(row, col);
46990         if(!el){
46991             return;
46992         }
46993         var c = this.scroller.dom;
46994
46995         var ctop = parseInt(el.offsetTop, 10);
46996         var cleft = parseInt(el.offsetLeft, 10);
46997         var cbot = ctop + el.offsetHeight;
46998         var cright = cleft + el.offsetWidth;
46999
47000         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47001         var stop = parseInt(c.scrollTop, 10);
47002         var sleft = parseInt(c.scrollLeft, 10);
47003         var sbot = stop + ch;
47004         var sright = sleft + c.clientWidth;
47005
47006         if(ctop < stop){
47007                 c.scrollTop = ctop;
47008         }else if(cbot > sbot){
47009             c.scrollTop = cbot-ch;
47010         }
47011
47012         if(hscroll !== false){
47013             if(cleft < sleft){
47014                 c.scrollLeft = cleft;
47015             }else if(cright > sright){
47016                 c.scrollLeft = cright-c.clientWidth;
47017             }
47018         }
47019         return el;
47020     },
47021
47022     updateColumns : function(){
47023         this.grid.stopEditing();
47024         var cm = this.grid.colModel, colIds = this.getColumnIds();
47025         //var totalWidth = cm.getTotalWidth();
47026         var pos = 0;
47027         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47028             //if(cm.isHidden(i)) continue;
47029             var w = cm.getColumnWidth(i);
47030             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47031             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47032         }
47033         this.updateSplitters();
47034     },
47035
47036     generateRules : function(cm){
47037         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47038         Roo.util.CSS.removeStyleSheet(rulesId);
47039         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47040             var cid = cm.getColumnId(i);
47041             var align = '';
47042             if(cm.config[i].align){
47043                 align = 'text-align:'+cm.config[i].align+';';
47044             }
47045             var hidden = '';
47046             if(cm.isHidden(i)){
47047                 hidden = 'display:none;';
47048             }
47049             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47050             ruleBuf.push(
47051                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47052                     this.hdSelector, cid, " {\n", align, width, "}\n",
47053                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47054                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47055         }
47056         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47057     },
47058
47059     updateSplitters : function(){
47060         var cm = this.cm, s = this.getSplitters();
47061         if(s){ // splitters not created yet
47062             var pos = 0, locked = true;
47063             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47064                 if(cm.isHidden(i)) continue;
47065                 var w = cm.getColumnWidth(i);
47066                 if(!cm.isLocked(i) && locked){
47067                     pos = 0;
47068                     locked = false;
47069                 }
47070                 pos += w;
47071                 s[i].style.left = (pos-this.splitOffset) + "px";
47072             }
47073         }
47074     },
47075
47076     handleHiddenChange : function(colModel, colIndex, hidden){
47077         if(hidden){
47078             this.hideColumn(colIndex);
47079         }else{
47080             this.unhideColumn(colIndex);
47081         }
47082     },
47083
47084     hideColumn : function(colIndex){
47085         var cid = this.getColumnId(colIndex);
47086         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47087         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47088         if(Roo.isSafari){
47089             this.updateHeaders();
47090         }
47091         this.updateSplitters();
47092         this.layout();
47093     },
47094
47095     unhideColumn : function(colIndex){
47096         var cid = this.getColumnId(colIndex);
47097         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47098         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47099
47100         if(Roo.isSafari){
47101             this.updateHeaders();
47102         }
47103         this.updateSplitters();
47104         this.layout();
47105     },
47106
47107     insertRows : function(dm, firstRow, lastRow, isUpdate){
47108         if(firstRow == 0 && lastRow == dm.getCount()-1){
47109             this.refresh();
47110         }else{
47111             if(!isUpdate){
47112                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47113             }
47114             var s = this.getScrollState();
47115             var markup = this.renderRows(firstRow, lastRow);
47116             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47117             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47118             this.restoreScroll(s);
47119             if(!isUpdate){
47120                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47121                 this.syncRowHeights(firstRow, lastRow);
47122                 this.stripeRows(firstRow);
47123                 this.layout();
47124             }
47125         }
47126     },
47127
47128     bufferRows : function(markup, target, index){
47129         var before = null, trows = target.rows, tbody = target.tBodies[0];
47130         if(index < trows.length){
47131             before = trows[index];
47132         }
47133         var b = document.createElement("div");
47134         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47135         var rows = b.firstChild.rows;
47136         for(var i = 0, len = rows.length; i < len; i++){
47137             if(before){
47138                 tbody.insertBefore(rows[0], before);
47139             }else{
47140                 tbody.appendChild(rows[0]);
47141             }
47142         }
47143         b.innerHTML = "";
47144         b = null;
47145     },
47146
47147     deleteRows : function(dm, firstRow, lastRow){
47148         if(dm.getRowCount()<1){
47149             this.fireEvent("beforerefresh", this);
47150             this.mainBody.update("");
47151             this.lockedBody.update("");
47152             this.fireEvent("refresh", this);
47153         }else{
47154             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47155             var bt = this.getBodyTable();
47156             var tbody = bt.firstChild;
47157             var rows = bt.rows;
47158             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47159                 tbody.removeChild(rows[firstRow]);
47160             }
47161             this.stripeRows(firstRow);
47162             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47163         }
47164     },
47165
47166     updateRows : function(dataSource, firstRow, lastRow){
47167         var s = this.getScrollState();
47168         this.refresh();
47169         this.restoreScroll(s);
47170     },
47171
47172     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47173         if(!noRefresh){
47174            this.refresh();
47175         }
47176         this.updateHeaderSortState();
47177     },
47178
47179     getScrollState : function(){
47180         var sb = this.scroller.dom;
47181         return {left: sb.scrollLeft, top: sb.scrollTop};
47182     },
47183
47184     stripeRows : function(startRow){
47185         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47186             return;
47187         }
47188         startRow = startRow || 0;
47189         var rows = this.getBodyTable().rows;
47190         var lrows = this.getLockedTable().rows;
47191         var cls = ' x-grid-row-alt ';
47192         for(var i = startRow, len = rows.length; i < len; i++){
47193             var row = rows[i], lrow = lrows[i];
47194             var isAlt = ((i+1) % 2 == 0);
47195             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47196             if(isAlt == hasAlt){
47197                 continue;
47198             }
47199             if(isAlt){
47200                 row.className += " x-grid-row-alt";
47201             }else{
47202                 row.className = row.className.replace("x-grid-row-alt", "");
47203             }
47204             if(lrow){
47205                 lrow.className = row.className;
47206             }
47207         }
47208     },
47209
47210     restoreScroll : function(state){
47211         var sb = this.scroller.dom;
47212         sb.scrollLeft = state.left;
47213         sb.scrollTop = state.top;
47214         this.syncScroll();
47215     },
47216
47217     syncScroll : function(){
47218         var sb = this.scroller.dom;
47219         var sh = this.mainHd.dom;
47220         var bs = this.mainBody.dom;
47221         var lv = this.lockedBody.dom;
47222         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47223         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47224     },
47225
47226     handleScroll : function(e){
47227         this.syncScroll();
47228         var sb = this.scroller.dom;
47229         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47230         e.stopEvent();
47231     },
47232
47233     handleWheel : function(e){
47234         var d = e.getWheelDelta();
47235         this.scroller.dom.scrollTop -= d*22;
47236         // set this here to prevent jumpy scrolling on large tables
47237         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47238         e.stopEvent();
47239     },
47240
47241     renderRows : function(startRow, endRow){
47242         // pull in all the crap needed to render rows
47243         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47244         var colCount = cm.getColumnCount();
47245
47246         if(ds.getCount() < 1){
47247             return ["", ""];
47248         }
47249
47250         // build a map for all the columns
47251         var cs = [];
47252         for(var i = 0; i < colCount; i++){
47253             var name = cm.getDataIndex(i);
47254             cs[i] = {
47255                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47256                 renderer : cm.getRenderer(i),
47257                 id : cm.getColumnId(i),
47258                 locked : cm.isLocked(i)
47259             };
47260         }
47261
47262         startRow = startRow || 0;
47263         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47264
47265         // records to render
47266         var rs = ds.getRange(startRow, endRow);
47267
47268         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47269     },
47270
47271     // As much as I hate to duplicate code, this was branched because FireFox really hates
47272     // [].join("") on strings. The performance difference was substantial enough to
47273     // branch this function
47274     doRender : Roo.isGecko ?
47275             function(cs, rs, ds, startRow, colCount, stripe){
47276                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47277                 // buffers
47278                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47279                 for(var j = 0, len = rs.length; j < len; j++){
47280                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47281                     for(var i = 0; i < colCount; i++){
47282                         c = cs[i];
47283                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47284                         p.id = c.id;
47285                         p.css = p.attr = "";
47286                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47287                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47288                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47289                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47290                         }
47291                         var markup = ct.apply(p);
47292                         if(!c.locked){
47293                             cb+= markup;
47294                         }else{
47295                             lcb+= markup;
47296                         }
47297                     }
47298                     var alt = [];
47299                     if(stripe && ((rowIndex+1) % 2 == 0)){
47300                         alt[0] = "x-grid-row-alt";
47301                     }
47302                     if(r.dirty){
47303                         alt[1] = " x-grid-dirty-row";
47304                     }
47305                     rp.cells = lcb;
47306                     if(this.getRowClass){
47307                         alt[2] = this.getRowClass(r, rowIndex);
47308                     }
47309                     rp.alt = alt.join(" ");
47310                     lbuf+= rt.apply(rp);
47311                     rp.cells = cb;
47312                     buf+=  rt.apply(rp);
47313                 }
47314                 return [lbuf, buf];
47315             } :
47316             function(cs, rs, ds, startRow, colCount, stripe){
47317                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47318                 // buffers
47319                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47320                 for(var j = 0, len = rs.length; j < len; j++){
47321                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47322                     for(var i = 0; i < colCount; i++){
47323                         c = cs[i];
47324                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47325                         p.id = c.id;
47326                         p.css = p.attr = "";
47327                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47328                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47329                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47330                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47331                         }
47332                         var markup = ct.apply(p);
47333                         if(!c.locked){
47334                             cb[cb.length] = markup;
47335                         }else{
47336                             lcb[lcb.length] = markup;
47337                         }
47338                     }
47339                     var alt = [];
47340                     if(stripe && ((rowIndex+1) % 2 == 0)){
47341                         alt[0] = "x-grid-row-alt";
47342                     }
47343                     if(r.dirty){
47344                         alt[1] = " x-grid-dirty-row";
47345                     }
47346                     rp.cells = lcb;
47347                     if(this.getRowClass){
47348                         alt[2] = this.getRowClass(r, rowIndex);
47349                     }
47350                     rp.alt = alt.join(" ");
47351                     rp.cells = lcb.join("");
47352                     lbuf[lbuf.length] = rt.apply(rp);
47353                     rp.cells = cb.join("");
47354                     buf[buf.length] =  rt.apply(rp);
47355                 }
47356                 return [lbuf.join(""), buf.join("")];
47357             },
47358
47359     renderBody : function(){
47360         var markup = this.renderRows();
47361         var bt = this.templates.body;
47362         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47363     },
47364
47365     /**
47366      * Refreshes the grid
47367      * @param {Boolean} headersToo
47368      */
47369     refresh : function(headersToo){
47370         this.fireEvent("beforerefresh", this);
47371         this.grid.stopEditing();
47372         var result = this.renderBody();
47373         this.lockedBody.update(result[0]);
47374         this.mainBody.update(result[1]);
47375         if(headersToo === true){
47376             this.updateHeaders();
47377             this.updateColumns();
47378             this.updateSplitters();
47379             this.updateHeaderSortState();
47380         }
47381         this.syncRowHeights();
47382         this.layout();
47383         this.fireEvent("refresh", this);
47384     },
47385
47386     handleColumnMove : function(cm, oldIndex, newIndex){
47387         this.indexMap = null;
47388         var s = this.getScrollState();
47389         this.refresh(true);
47390         this.restoreScroll(s);
47391         this.afterMove(newIndex);
47392     },
47393
47394     afterMove : function(colIndex){
47395         if(this.enableMoveAnim && Roo.enableFx){
47396             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47397         }
47398     },
47399
47400     updateCell : function(dm, rowIndex, dataIndex){
47401         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47402         if(typeof colIndex == "undefined"){ // not present in grid
47403             return;
47404         }
47405         var cm = this.grid.colModel;
47406         var cell = this.getCell(rowIndex, colIndex);
47407         var cellText = this.getCellText(rowIndex, colIndex);
47408
47409         var p = {
47410             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47411             id : cm.getColumnId(colIndex),
47412             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47413         };
47414         var renderer = cm.getRenderer(colIndex);
47415         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47416         if(typeof val == "undefined" || val === "") val = "&#160;";
47417         cellText.innerHTML = val;
47418         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47419         this.syncRowHeights(rowIndex, rowIndex);
47420     },
47421
47422     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47423         var maxWidth = 0;
47424         if(this.grid.autoSizeHeaders){
47425             var h = this.getHeaderCellMeasure(colIndex);
47426             maxWidth = Math.max(maxWidth, h.scrollWidth);
47427         }
47428         var tb, index;
47429         if(this.cm.isLocked(colIndex)){
47430             tb = this.getLockedTable();
47431             index = colIndex;
47432         }else{
47433             tb = this.getBodyTable();
47434             index = colIndex - this.cm.getLockedCount();
47435         }
47436         if(tb && tb.rows){
47437             var rows = tb.rows;
47438             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47439             for(var i = 0; i < stopIndex; i++){
47440                 var cell = rows[i].childNodes[index].firstChild;
47441                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47442             }
47443         }
47444         return maxWidth + /*margin for error in IE*/ 5;
47445     },
47446     /**
47447      * Autofit a column to its content.
47448      * @param {Number} colIndex
47449      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47450      */
47451      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47452          if(this.cm.isHidden(colIndex)){
47453              return; // can't calc a hidden column
47454          }
47455         if(forceMinSize){
47456             var cid = this.cm.getColumnId(colIndex);
47457             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47458            if(this.grid.autoSizeHeaders){
47459                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47460            }
47461         }
47462         var newWidth = this.calcColumnWidth(colIndex);
47463         this.cm.setColumnWidth(colIndex,
47464             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47465         if(!suppressEvent){
47466             this.grid.fireEvent("columnresize", colIndex, newWidth);
47467         }
47468     },
47469
47470     /**
47471      * Autofits all columns to their content and then expands to fit any extra space in the grid
47472      */
47473      autoSizeColumns : function(){
47474         var cm = this.grid.colModel;
47475         var colCount = cm.getColumnCount();
47476         for(var i = 0; i < colCount; i++){
47477             this.autoSizeColumn(i, true, true);
47478         }
47479         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47480             this.fitColumns();
47481         }else{
47482             this.updateColumns();
47483             this.layout();
47484         }
47485     },
47486
47487     /**
47488      * Autofits all columns to the grid's width proportionate with their current size
47489      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47490      */
47491     fitColumns : function(reserveScrollSpace){
47492         var cm = this.grid.colModel;
47493         var colCount = cm.getColumnCount();
47494         var cols = [];
47495         var width = 0;
47496         var i, w;
47497         for (i = 0; i < colCount; i++){
47498             if(!cm.isHidden(i) && !cm.isFixed(i)){
47499                 w = cm.getColumnWidth(i);
47500                 cols.push(i);
47501                 cols.push(w);
47502                 width += w;
47503             }
47504         }
47505         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47506         if(reserveScrollSpace){
47507             avail -= 17;
47508         }
47509         var frac = (avail - cm.getTotalWidth())/width;
47510         while (cols.length){
47511             w = cols.pop();
47512             i = cols.pop();
47513             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47514         }
47515         this.updateColumns();
47516         this.layout();
47517     },
47518
47519     onRowSelect : function(rowIndex){
47520         var row = this.getRowComposite(rowIndex);
47521         row.addClass("x-grid-row-selected");
47522     },
47523
47524     onRowDeselect : function(rowIndex){
47525         var row = this.getRowComposite(rowIndex);
47526         row.removeClass("x-grid-row-selected");
47527     },
47528
47529     onCellSelect : function(row, col){
47530         var cell = this.getCell(row, col);
47531         if(cell){
47532             Roo.fly(cell).addClass("x-grid-cell-selected");
47533         }
47534     },
47535
47536     onCellDeselect : function(row, col){
47537         var cell = this.getCell(row, col);
47538         if(cell){
47539             Roo.fly(cell).removeClass("x-grid-cell-selected");
47540         }
47541     },
47542
47543     updateHeaderSortState : function(){
47544         var state = this.ds.getSortState();
47545         if(!state){
47546             return;
47547         }
47548         this.sortState = state;
47549         var sortColumn = this.cm.findColumnIndex(state.field);
47550         if(sortColumn != -1){
47551             var sortDir = state.direction;
47552             var sc = this.sortClasses;
47553             var hds = this.el.select(this.headerSelector).removeClass(sc);
47554             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47555         }
47556     },
47557
47558     handleHeaderClick : function(g, index){
47559         if(this.headersDisabled){
47560             return;
47561         }
47562         var dm = g.dataSource, cm = g.colModel;
47563             if(!cm.isSortable(index)){
47564             return;
47565         }
47566             g.stopEditing();
47567         dm.sort(cm.getDataIndex(index));
47568     },
47569
47570
47571     destroy : function(){
47572         if(this.colMenu){
47573             this.colMenu.removeAll();
47574             Roo.menu.MenuMgr.unregister(this.colMenu);
47575             this.colMenu.getEl().remove();
47576             delete this.colMenu;
47577         }
47578         if(this.hmenu){
47579             this.hmenu.removeAll();
47580             Roo.menu.MenuMgr.unregister(this.hmenu);
47581             this.hmenu.getEl().remove();
47582             delete this.hmenu;
47583         }
47584         if(this.grid.enableColumnMove){
47585             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47586             if(dds){
47587                 for(var dd in dds){
47588                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47589                         var elid = dds[dd].dragElId;
47590                         dds[dd].unreg();
47591                         Roo.get(elid).remove();
47592                     } else if(dds[dd].config.isTarget){
47593                         dds[dd].proxyTop.remove();
47594                         dds[dd].proxyBottom.remove();
47595                         dds[dd].unreg();
47596                     }
47597                     if(Roo.dd.DDM.locationCache[dd]){
47598                         delete Roo.dd.DDM.locationCache[dd];
47599                     }
47600                 }
47601                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47602             }
47603         }
47604         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47605         this.bind(null, null);
47606         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47607     },
47608
47609     handleLockChange : function(){
47610         this.refresh(true);
47611     },
47612
47613     onDenyColumnLock : function(){
47614
47615     },
47616
47617     onDenyColumnHide : function(){
47618
47619     },
47620
47621     handleHdMenuClick : function(item){
47622         var index = this.hdCtxIndex;
47623         var cm = this.cm, ds = this.ds;
47624         switch(item.id){
47625             case "asc":
47626                 ds.sort(cm.getDataIndex(index), "ASC");
47627                 break;
47628             case "desc":
47629                 ds.sort(cm.getDataIndex(index), "DESC");
47630                 break;
47631             case "lock":
47632                 var lc = cm.getLockedCount();
47633                 if(cm.getColumnCount(true) <= lc+1){
47634                     this.onDenyColumnLock();
47635                     return;
47636                 }
47637                 if(lc != index){
47638                     cm.setLocked(index, true, true);
47639                     cm.moveColumn(index, lc);
47640                     this.grid.fireEvent("columnmove", index, lc);
47641                 }else{
47642                     cm.setLocked(index, true);
47643                 }
47644             break;
47645             case "unlock":
47646                 var lc = cm.getLockedCount();
47647                 if((lc-1) != index){
47648                     cm.setLocked(index, false, true);
47649                     cm.moveColumn(index, lc-1);
47650                     this.grid.fireEvent("columnmove", index, lc-1);
47651                 }else{
47652                     cm.setLocked(index, false);
47653                 }
47654             break;
47655             default:
47656                 index = cm.getIndexById(item.id.substr(4));
47657                 if(index != -1){
47658                     if(item.checked && cm.getColumnCount(true) <= 1){
47659                         this.onDenyColumnHide();
47660                         return false;
47661                     }
47662                     cm.setHidden(index, item.checked);
47663                 }
47664         }
47665         return true;
47666     },
47667
47668     beforeColMenuShow : function(){
47669         var cm = this.cm,  colCount = cm.getColumnCount();
47670         this.colMenu.removeAll();
47671         for(var i = 0; i < colCount; i++){
47672             this.colMenu.add(new Roo.menu.CheckItem({
47673                 id: "col-"+cm.getColumnId(i),
47674                 text: cm.getColumnHeader(i),
47675                 checked: !cm.isHidden(i),
47676                 hideOnClick:false
47677             }));
47678         }
47679     },
47680
47681     handleHdCtx : function(g, index, e){
47682         e.stopEvent();
47683         var hd = this.getHeaderCell(index);
47684         this.hdCtxIndex = index;
47685         var ms = this.hmenu.items, cm = this.cm;
47686         ms.get("asc").setDisabled(!cm.isSortable(index));
47687         ms.get("desc").setDisabled(!cm.isSortable(index));
47688         if(this.grid.enableColLock !== false){
47689             ms.get("lock").setDisabled(cm.isLocked(index));
47690             ms.get("unlock").setDisabled(!cm.isLocked(index));
47691         }
47692         this.hmenu.show(hd, "tl-bl");
47693     },
47694
47695     handleHdOver : function(e){
47696         var hd = this.findHeaderCell(e.getTarget());
47697         if(hd && !this.headersDisabled){
47698             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47699                this.fly(hd).addClass("x-grid-hd-over");
47700             }
47701         }
47702     },
47703
47704     handleHdOut : function(e){
47705         var hd = this.findHeaderCell(e.getTarget());
47706         if(hd){
47707             this.fly(hd).removeClass("x-grid-hd-over");
47708         }
47709     },
47710
47711     handleSplitDblClick : function(e, t){
47712         var i = this.getCellIndex(t);
47713         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47714             this.autoSizeColumn(i, true);
47715             this.layout();
47716         }
47717     },
47718
47719     render : function(){
47720
47721         var cm = this.cm;
47722         var colCount = cm.getColumnCount();
47723
47724         if(this.grid.monitorWindowResize === true){
47725             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47726         }
47727         var header = this.renderHeaders();
47728         var body = this.templates.body.apply({rows:""});
47729         var html = this.templates.master.apply({
47730             lockedBody: body,
47731             body: body,
47732             lockedHeader: header[0],
47733             header: header[1]
47734         });
47735
47736         //this.updateColumns();
47737
47738         this.grid.getGridEl().dom.innerHTML = html;
47739
47740         this.initElements();
47741
47742         this.scroller.on("scroll", this.handleScroll, this);
47743         this.lockedBody.on("mousewheel", this.handleWheel, this);
47744         this.mainBody.on("mousewheel", this.handleWheel, this);
47745
47746         this.mainHd.on("mouseover", this.handleHdOver, this);
47747         this.mainHd.on("mouseout", this.handleHdOut, this);
47748         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47749                 {delegate: "."+this.splitClass});
47750
47751         this.lockedHd.on("mouseover", this.handleHdOver, this);
47752         this.lockedHd.on("mouseout", this.handleHdOut, this);
47753         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47754                 {delegate: "."+this.splitClass});
47755
47756         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47757             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47758         }
47759
47760         this.updateSplitters();
47761
47762         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47763             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47764             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47765         }
47766
47767         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47768             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47769             this.hmenu.add(
47770                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47771                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47772             );
47773             if(this.grid.enableColLock !== false){
47774                 this.hmenu.add('-',
47775                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47776                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47777                 );
47778             }
47779             if(this.grid.enableColumnHide !== false){
47780
47781                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47782                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47783                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47784
47785                 this.hmenu.add('-',
47786                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47787                 );
47788             }
47789             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47790
47791             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47792         }
47793
47794         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47795             this.dd = new Roo.grid.GridDragZone(this.grid, {
47796                 ddGroup : this.grid.ddGroup || 'GridDD'
47797             });
47798         }
47799
47800         /*
47801         for(var i = 0; i < colCount; i++){
47802             if(cm.isHidden(i)){
47803                 this.hideColumn(i);
47804             }
47805             if(cm.config[i].align){
47806                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47807                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47808             }
47809         }*/
47810         
47811         this.updateHeaderSortState();
47812
47813         this.beforeInitialResize();
47814         this.layout(true);
47815
47816         // two part rendering gives faster view to the user
47817         this.renderPhase2.defer(1, this);
47818     },
47819
47820     renderPhase2 : function(){
47821         // render the rows now
47822         this.refresh();
47823         if(this.grid.autoSizeColumns){
47824             this.autoSizeColumns();
47825         }
47826     },
47827
47828     beforeInitialResize : function(){
47829
47830     },
47831
47832     onColumnSplitterMoved : function(i, w){
47833         this.userResized = true;
47834         var cm = this.grid.colModel;
47835         cm.setColumnWidth(i, w, true);
47836         var cid = cm.getColumnId(i);
47837         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47838         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47839         this.updateSplitters();
47840         this.layout();
47841         this.grid.fireEvent("columnresize", i, w);
47842     },
47843
47844     syncRowHeights : function(startIndex, endIndex){
47845         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47846             startIndex = startIndex || 0;
47847             var mrows = this.getBodyTable().rows;
47848             var lrows = this.getLockedTable().rows;
47849             var len = mrows.length-1;
47850             endIndex = Math.min(endIndex || len, len);
47851             for(var i = startIndex; i <= endIndex; i++){
47852                 var m = mrows[i], l = lrows[i];
47853                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47854                 m.style.height = l.style.height = h + "px";
47855             }
47856         }
47857     },
47858
47859     layout : function(initialRender, is2ndPass){
47860         var g = this.grid;
47861         var auto = g.autoHeight;
47862         var scrollOffset = 16;
47863         var c = g.getGridEl(), cm = this.cm,
47864                 expandCol = g.autoExpandColumn,
47865                 gv = this;
47866         //c.beginMeasure();
47867
47868         if(!c.dom.offsetWidth){ // display:none?
47869             if(initialRender){
47870                 this.lockedWrap.show();
47871                 this.mainWrap.show();
47872             }
47873             return;
47874         }
47875
47876         var hasLock = this.cm.isLocked(0);
47877
47878         var tbh = this.headerPanel.getHeight();
47879         var bbh = this.footerPanel.getHeight();
47880
47881         if(auto){
47882             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47883             var newHeight = ch + c.getBorderWidth("tb");
47884             if(g.maxHeight){
47885                 newHeight = Math.min(g.maxHeight, newHeight);
47886             }
47887             c.setHeight(newHeight);
47888         }
47889
47890         if(g.autoWidth){
47891             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47892         }
47893
47894         var s = this.scroller;
47895
47896         var csize = c.getSize(true);
47897
47898         this.el.setSize(csize.width, csize.height);
47899
47900         this.headerPanel.setWidth(csize.width);
47901         this.footerPanel.setWidth(csize.width);
47902
47903         var hdHeight = this.mainHd.getHeight();
47904         var vw = csize.width;
47905         var vh = csize.height - (tbh + bbh);
47906
47907         s.setSize(vw, vh);
47908
47909         var bt = this.getBodyTable();
47910         var ltWidth = hasLock ?
47911                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47912
47913         var scrollHeight = bt.offsetHeight;
47914         var scrollWidth = ltWidth + bt.offsetWidth;
47915         var vscroll = false, hscroll = false;
47916
47917         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47918
47919         var lw = this.lockedWrap, mw = this.mainWrap;
47920         var lb = this.lockedBody, mb = this.mainBody;
47921
47922         setTimeout(function(){
47923             var t = s.dom.offsetTop;
47924             var w = s.dom.clientWidth,
47925                 h = s.dom.clientHeight;
47926
47927             lw.setTop(t);
47928             lw.setSize(ltWidth, h);
47929
47930             mw.setLeftTop(ltWidth, t);
47931             mw.setSize(w-ltWidth, h);
47932
47933             lb.setHeight(h-hdHeight);
47934             mb.setHeight(h-hdHeight);
47935
47936             if(is2ndPass !== true && !gv.userResized && expandCol){
47937                 // high speed resize without full column calculation
47938                 
47939                 var ci = cm.getIndexById(expandCol);
47940                 if (ci < 0) {
47941                     ci = cm.findColumnIndex(expandCol);
47942                 }
47943                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47944                 var expandId = cm.getColumnId(ci);
47945                 var  tw = cm.getTotalWidth(false);
47946                 var currentWidth = cm.getColumnWidth(ci);
47947                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47948                 if(currentWidth != cw){
47949                     cm.setColumnWidth(ci, cw, true);
47950                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47951                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47952                     gv.updateSplitters();
47953                     gv.layout(false, true);
47954                 }
47955             }
47956
47957             if(initialRender){
47958                 lw.show();
47959                 mw.show();
47960             }
47961             //c.endMeasure();
47962         }, 10);
47963     },
47964
47965     onWindowResize : function(){
47966         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47967             return;
47968         }
47969         this.layout();
47970     },
47971
47972     appendFooter : function(parentEl){
47973         return null;
47974     },
47975
47976     sortAscText : "Sort Ascending",
47977     sortDescText : "Sort Descending",
47978     lockText : "Lock Column",
47979     unlockText : "Unlock Column",
47980     columnsText : "Columns"
47981 });
47982
47983
47984 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47985     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47986     this.proxy.el.addClass('x-grid3-col-dd');
47987 };
47988
47989 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47990     handleMouseDown : function(e){
47991
47992     },
47993
47994     callHandleMouseDown : function(e){
47995         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47996     }
47997 });
47998 /*
47999  * Based on:
48000  * Ext JS Library 1.1.1
48001  * Copyright(c) 2006-2007, Ext JS, LLC.
48002  *
48003  * Originally Released Under LGPL - original licence link has changed is not relivant.
48004  *
48005  * Fork - LGPL
48006  * <script type="text/javascript">
48007  */
48008  
48009 // private
48010 // This is a support class used internally by the Grid components
48011 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48012     this.grid = grid;
48013     this.view = grid.getView();
48014     this.proxy = this.view.resizeProxy;
48015     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48016         "gridSplitters" + this.grid.getGridEl().id, {
48017         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48018     });
48019     this.setHandleElId(Roo.id(hd));
48020     this.setOuterHandleElId(Roo.id(hd2));
48021     this.scroll = false;
48022 };
48023 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48024     fly: Roo.Element.fly,
48025
48026     b4StartDrag : function(x, y){
48027         this.view.headersDisabled = true;
48028         this.proxy.setHeight(this.view.mainWrap.getHeight());
48029         var w = this.cm.getColumnWidth(this.cellIndex);
48030         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48031         this.resetConstraints();
48032         this.setXConstraint(minw, 1000);
48033         this.setYConstraint(0, 0);
48034         this.minX = x - minw;
48035         this.maxX = x + 1000;
48036         this.startPos = x;
48037         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48038     },
48039
48040
48041     handleMouseDown : function(e){
48042         ev = Roo.EventObject.setEvent(e);
48043         var t = this.fly(ev.getTarget());
48044         if(t.hasClass("x-grid-split")){
48045             this.cellIndex = this.view.getCellIndex(t.dom);
48046             this.split = t.dom;
48047             this.cm = this.grid.colModel;
48048             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48049                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48050             }
48051         }
48052     },
48053
48054     endDrag : function(e){
48055         this.view.headersDisabled = false;
48056         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48057         var diff = endX - this.startPos;
48058         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48059     },
48060
48061     autoOffset : function(){
48062         this.setDelta(0,0);
48063     }
48064 });/*
48065  * Based on:
48066  * Ext JS Library 1.1.1
48067  * Copyright(c) 2006-2007, Ext JS, LLC.
48068  *
48069  * Originally Released Under LGPL - original licence link has changed is not relivant.
48070  *
48071  * Fork - LGPL
48072  * <script type="text/javascript">
48073  */
48074  
48075 // private
48076 // This is a support class used internally by the Grid components
48077 Roo.grid.GridDragZone = function(grid, config){
48078     this.view = grid.getView();
48079     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48080     if(this.view.lockedBody){
48081         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48082         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48083     }
48084     this.scroll = false;
48085     this.grid = grid;
48086     this.ddel = document.createElement('div');
48087     this.ddel.className = 'x-grid-dd-wrap';
48088 };
48089
48090 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48091     ddGroup : "GridDD",
48092
48093     getDragData : function(e){
48094         var t = Roo.lib.Event.getTarget(e);
48095         var rowIndex = this.view.findRowIndex(t);
48096         if(rowIndex !== false){
48097             var sm = this.grid.selModel;
48098             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48099               //  sm.mouseDown(e, t);
48100             //}
48101             if (e.hasModifier()){
48102                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48103             }
48104             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48105         }
48106         return false;
48107     },
48108
48109     onInitDrag : function(e){
48110         var data = this.dragData;
48111         this.ddel.innerHTML = this.grid.getDragDropText();
48112         this.proxy.update(this.ddel);
48113         // fire start drag?
48114     },
48115
48116     afterRepair : function(){
48117         this.dragging = false;
48118     },
48119
48120     getRepairXY : function(e, data){
48121         return false;
48122     },
48123
48124     onEndDrag : function(data, e){
48125         // fire end drag?
48126     },
48127
48128     onValidDrop : function(dd, e, id){
48129         // fire drag drop?
48130         this.hideProxy();
48131     },
48132
48133     beforeInvalidDrop : function(e, id){
48134
48135     }
48136 });/*
48137  * Based on:
48138  * Ext JS Library 1.1.1
48139  * Copyright(c) 2006-2007, Ext JS, LLC.
48140  *
48141  * Originally Released Under LGPL - original licence link has changed is not relivant.
48142  *
48143  * Fork - LGPL
48144  * <script type="text/javascript">
48145  */
48146  
48147
48148 /**
48149  * @class Roo.grid.ColumnModel
48150  * @extends Roo.util.Observable
48151  * This is the default implementation of a ColumnModel used by the Grid. It defines
48152  * the columns in the grid.
48153  * <br>Usage:<br>
48154  <pre><code>
48155  var colModel = new Roo.grid.ColumnModel([
48156         {header: "Ticker", width: 60, sortable: true, locked: true},
48157         {header: "Company Name", width: 150, sortable: true},
48158         {header: "Market Cap.", width: 100, sortable: true},
48159         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48160         {header: "Employees", width: 100, sortable: true, resizable: false}
48161  ]);
48162  </code></pre>
48163  * <p>
48164  
48165  * The config options listed for this class are options which may appear in each
48166  * individual column definition.
48167  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48168  * @constructor
48169  * @param {Object} config An Array of column config objects. See this class's
48170  * config objects for details.
48171 */
48172 Roo.grid.ColumnModel = function(config){
48173         /**
48174      * The config passed into the constructor
48175      */
48176     this.config = config;
48177     this.lookup = {};
48178
48179     // if no id, create one
48180     // if the column does not have a dataIndex mapping,
48181     // map it to the order it is in the config
48182     for(var i = 0, len = config.length; i < len; i++){
48183         var c = config[i];
48184         if(typeof c.dataIndex == "undefined"){
48185             c.dataIndex = i;
48186         }
48187         if(typeof c.renderer == "string"){
48188             c.renderer = Roo.util.Format[c.renderer];
48189         }
48190         if(typeof c.id == "undefined"){
48191             c.id = Roo.id();
48192         }
48193         if(c.editor && c.editor.xtype){
48194             c.editor  = Roo.factory(c.editor, Roo.grid);
48195         }
48196         if(c.editor && c.editor.isFormField){
48197             c.editor = new Roo.grid.GridEditor(c.editor);
48198         }
48199         this.lookup[c.id] = c;
48200     }
48201
48202     /**
48203      * The width of columns which have no width specified (defaults to 100)
48204      * @type Number
48205      */
48206     this.defaultWidth = 100;
48207
48208     /**
48209      * Default sortable of columns which have no sortable specified (defaults to false)
48210      * @type Boolean
48211      */
48212     this.defaultSortable = false;
48213
48214     this.addEvents({
48215         /**
48216              * @event widthchange
48217              * Fires when the width of a column changes.
48218              * @param {ColumnModel} this
48219              * @param {Number} columnIndex The column index
48220              * @param {Number} newWidth The new width
48221              */
48222             "widthchange": true,
48223         /**
48224              * @event headerchange
48225              * Fires when the text of a header changes.
48226              * @param {ColumnModel} this
48227              * @param {Number} columnIndex The column index
48228              * @param {Number} newText The new header text
48229              */
48230             "headerchange": true,
48231         /**
48232              * @event hiddenchange
48233              * Fires when a column is hidden or "unhidden".
48234              * @param {ColumnModel} this
48235              * @param {Number} columnIndex The column index
48236              * @param {Boolean} hidden true if hidden, false otherwise
48237              */
48238             "hiddenchange": true,
48239             /**
48240          * @event columnmoved
48241          * Fires when a column is moved.
48242          * @param {ColumnModel} this
48243          * @param {Number} oldIndex
48244          * @param {Number} newIndex
48245          */
48246         "columnmoved" : true,
48247         /**
48248          * @event columlockchange
48249          * Fires when a column's locked state is changed
48250          * @param {ColumnModel} this
48251          * @param {Number} colIndex
48252          * @param {Boolean} locked true if locked
48253          */
48254         "columnlockchange" : true
48255     });
48256     Roo.grid.ColumnModel.superclass.constructor.call(this);
48257 };
48258 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48259     /**
48260      * @cfg {String} header The header text to display in the Grid view.
48261      */
48262     /**
48263      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48264      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48265      * specified, the column's index is used as an index into the Record's data Array.
48266      */
48267     /**
48268      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48269      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48270      */
48271     /**
48272      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48273      * Defaults to the value of the {@link #defaultSortable} property.
48274      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48275      */
48276     /**
48277      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48278      */
48279     /**
48280      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48281      */
48282     /**
48283      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48284      */
48285     /**
48286      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48287      */
48288     /**
48289      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48290      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48291      * default renderer uses the raw data value.
48292      */
48293        /**
48294      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48295      */
48296     /**
48297      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48298      */
48299
48300     /**
48301      * Returns the id of the column at the specified index.
48302      * @param {Number} index The column index
48303      * @return {String} the id
48304      */
48305     getColumnId : function(index){
48306         return this.config[index].id;
48307     },
48308
48309     /**
48310      * Returns the column for a specified id.
48311      * @param {String} id The column id
48312      * @return {Object} the column
48313      */
48314     getColumnById : function(id){
48315         return this.lookup[id];
48316     },
48317
48318     
48319     /**
48320      * Returns the column for a specified dataIndex.
48321      * @param {String} dataIndex The column dataIndex
48322      * @return {Object|Boolean} the column or false if not found
48323      */
48324     getColumnByDataIndex: function(dataIndex){
48325         var index = this.findColumnIndex(dataIndex);
48326         return index > -1 ? this.config[index] : false;
48327     },
48328     
48329     /**
48330      * Returns the index for a specified column id.
48331      * @param {String} id The column id
48332      * @return {Number} the index, or -1 if not found
48333      */
48334     getIndexById : function(id){
48335         for(var i = 0, len = this.config.length; i < len; i++){
48336             if(this.config[i].id == id){
48337                 return i;
48338             }
48339         }
48340         return -1;
48341     },
48342     
48343     /**
48344      * Returns the index for a specified column dataIndex.
48345      * @param {String} dataIndex The column dataIndex
48346      * @return {Number} the index, or -1 if not found
48347      */
48348     
48349     findColumnIndex : function(dataIndex){
48350         for(var i = 0, len = this.config.length; i < len; i++){
48351             if(this.config[i].dataIndex == dataIndex){
48352                 return i;
48353             }
48354         }
48355         return -1;
48356     },
48357     
48358     
48359     moveColumn : function(oldIndex, newIndex){
48360         var c = this.config[oldIndex];
48361         this.config.splice(oldIndex, 1);
48362         this.config.splice(newIndex, 0, c);
48363         this.dataMap = null;
48364         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48365     },
48366
48367     isLocked : function(colIndex){
48368         return this.config[colIndex].locked === true;
48369     },
48370
48371     setLocked : function(colIndex, value, suppressEvent){
48372         if(this.isLocked(colIndex) == value){
48373             return;
48374         }
48375         this.config[colIndex].locked = value;
48376         if(!suppressEvent){
48377             this.fireEvent("columnlockchange", this, colIndex, value);
48378         }
48379     },
48380
48381     getTotalLockedWidth : function(){
48382         var totalWidth = 0;
48383         for(var i = 0; i < this.config.length; i++){
48384             if(this.isLocked(i) && !this.isHidden(i)){
48385                 this.totalWidth += this.getColumnWidth(i);
48386             }
48387         }
48388         return totalWidth;
48389     },
48390
48391     getLockedCount : function(){
48392         for(var i = 0, len = this.config.length; i < len; i++){
48393             if(!this.isLocked(i)){
48394                 return i;
48395             }
48396         }
48397     },
48398
48399     /**
48400      * Returns the number of columns.
48401      * @return {Number}
48402      */
48403     getColumnCount : function(visibleOnly){
48404         if(visibleOnly === true){
48405             var c = 0;
48406             for(var i = 0, len = this.config.length; i < len; i++){
48407                 if(!this.isHidden(i)){
48408                     c++;
48409                 }
48410             }
48411             return c;
48412         }
48413         return this.config.length;
48414     },
48415
48416     /**
48417      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48418      * @param {Function} fn
48419      * @param {Object} scope (optional)
48420      * @return {Array} result
48421      */
48422     getColumnsBy : function(fn, scope){
48423         var r = [];
48424         for(var i = 0, len = this.config.length; i < len; i++){
48425             var c = this.config[i];
48426             if(fn.call(scope||this, c, i) === true){
48427                 r[r.length] = c;
48428             }
48429         }
48430         return r;
48431     },
48432
48433     /**
48434      * Returns true if the specified column is sortable.
48435      * @param {Number} col The column index
48436      * @return {Boolean}
48437      */
48438     isSortable : function(col){
48439         if(typeof this.config[col].sortable == "undefined"){
48440             return this.defaultSortable;
48441         }
48442         return this.config[col].sortable;
48443     },
48444
48445     /**
48446      * Returns the rendering (formatting) function defined for the column.
48447      * @param {Number} col The column index.
48448      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48449      */
48450     getRenderer : function(col){
48451         if(!this.config[col].renderer){
48452             return Roo.grid.ColumnModel.defaultRenderer;
48453         }
48454         return this.config[col].renderer;
48455     },
48456
48457     /**
48458      * Sets the rendering (formatting) function for a column.
48459      * @param {Number} col The column index
48460      * @param {Function} fn The function to use to process the cell's raw data
48461      * to return HTML markup for the grid view. The render function is called with
48462      * the following parameters:<ul>
48463      * <li>Data value.</li>
48464      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48465      * <li>css A CSS style string to apply to the table cell.</li>
48466      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48467      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48468      * <li>Row index</li>
48469      * <li>Column index</li>
48470      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48471      */
48472     setRenderer : function(col, fn){
48473         this.config[col].renderer = fn;
48474     },
48475
48476     /**
48477      * Returns the width for the specified column.
48478      * @param {Number} col The column index
48479      * @return {Number}
48480      */
48481     getColumnWidth : function(col){
48482         return this.config[col].width || this.defaultWidth;
48483     },
48484
48485     /**
48486      * Sets the width for a column.
48487      * @param {Number} col The column index
48488      * @param {Number} width The new width
48489      */
48490     setColumnWidth : function(col, width, suppressEvent){
48491         this.config[col].width = width;
48492         this.totalWidth = null;
48493         if(!suppressEvent){
48494              this.fireEvent("widthchange", this, col, width);
48495         }
48496     },
48497
48498     /**
48499      * Returns the total width of all columns.
48500      * @param {Boolean} includeHidden True to include hidden column widths
48501      * @return {Number}
48502      */
48503     getTotalWidth : function(includeHidden){
48504         if(!this.totalWidth){
48505             this.totalWidth = 0;
48506             for(var i = 0, len = this.config.length; i < len; i++){
48507                 if(includeHidden || !this.isHidden(i)){
48508                     this.totalWidth += this.getColumnWidth(i);
48509                 }
48510             }
48511         }
48512         return this.totalWidth;
48513     },
48514
48515     /**
48516      * Returns the header for the specified column.
48517      * @param {Number} col The column index
48518      * @return {String}
48519      */
48520     getColumnHeader : function(col){
48521         return this.config[col].header;
48522     },
48523
48524     /**
48525      * Sets the header for a column.
48526      * @param {Number} col The column index
48527      * @param {String} header The new header
48528      */
48529     setColumnHeader : function(col, header){
48530         this.config[col].header = header;
48531         this.fireEvent("headerchange", this, col, header);
48532     },
48533
48534     /**
48535      * Returns the tooltip for the specified column.
48536      * @param {Number} col The column index
48537      * @return {String}
48538      */
48539     getColumnTooltip : function(col){
48540             return this.config[col].tooltip;
48541     },
48542     /**
48543      * Sets the tooltip for a column.
48544      * @param {Number} col The column index
48545      * @param {String} tooltip The new tooltip
48546      */
48547     setColumnTooltip : function(col, tooltip){
48548             this.config[col].tooltip = tooltip;
48549     },
48550
48551     /**
48552      * Returns the dataIndex for the specified column.
48553      * @param {Number} col The column index
48554      * @return {Number}
48555      */
48556     getDataIndex : function(col){
48557         return this.config[col].dataIndex;
48558     },
48559
48560     /**
48561      * Sets the dataIndex for a column.
48562      * @param {Number} col The column index
48563      * @param {Number} dataIndex The new dataIndex
48564      */
48565     setDataIndex : function(col, dataIndex){
48566         this.config[col].dataIndex = dataIndex;
48567     },
48568
48569     
48570     
48571     /**
48572      * Returns true if the cell is editable.
48573      * @param {Number} colIndex The column index
48574      * @param {Number} rowIndex The row index
48575      * @return {Boolean}
48576      */
48577     isCellEditable : function(colIndex, rowIndex){
48578         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48579     },
48580
48581     /**
48582      * Returns the editor defined for the cell/column.
48583      * return false or null to disable editing.
48584      * @param {Number} colIndex The column index
48585      * @param {Number} rowIndex The row index
48586      * @return {Object}
48587      */
48588     getCellEditor : function(colIndex, rowIndex){
48589         return this.config[colIndex].editor;
48590     },
48591
48592     /**
48593      * Sets if a column is editable.
48594      * @param {Number} col The column index
48595      * @param {Boolean} editable True if the column is editable
48596      */
48597     setEditable : function(col, editable){
48598         this.config[col].editable = editable;
48599     },
48600
48601
48602     /**
48603      * Returns true if the column is hidden.
48604      * @param {Number} colIndex The column index
48605      * @return {Boolean}
48606      */
48607     isHidden : function(colIndex){
48608         return this.config[colIndex].hidden;
48609     },
48610
48611
48612     /**
48613      * Returns true if the column width cannot be changed
48614      */
48615     isFixed : function(colIndex){
48616         return this.config[colIndex].fixed;
48617     },
48618
48619     /**
48620      * Returns true if the column can be resized
48621      * @return {Boolean}
48622      */
48623     isResizable : function(colIndex){
48624         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48625     },
48626     /**
48627      * Sets if a column is hidden.
48628      * @param {Number} colIndex The column index
48629      * @param {Boolean} hidden True if the column is hidden
48630      */
48631     setHidden : function(colIndex, hidden){
48632         this.config[colIndex].hidden = hidden;
48633         this.totalWidth = null;
48634         this.fireEvent("hiddenchange", this, colIndex, hidden);
48635     },
48636
48637     /**
48638      * Sets the editor for a column.
48639      * @param {Number} col The column index
48640      * @param {Object} editor The editor object
48641      */
48642     setEditor : function(col, editor){
48643         this.config[col].editor = editor;
48644     }
48645 });
48646
48647 Roo.grid.ColumnModel.defaultRenderer = function(value){
48648         if(typeof value == "string" && value.length < 1){
48649             return "&#160;";
48650         }
48651         return value;
48652 };
48653
48654 // Alias for backwards compatibility
48655 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48656 /*
48657  * Based on:
48658  * Ext JS Library 1.1.1
48659  * Copyright(c) 2006-2007, Ext JS, LLC.
48660  *
48661  * Originally Released Under LGPL - original licence link has changed is not relivant.
48662  *
48663  * Fork - LGPL
48664  * <script type="text/javascript">
48665  */
48666
48667 /**
48668  * @class Roo.grid.AbstractSelectionModel
48669  * @extends Roo.util.Observable
48670  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48671  * implemented by descendant classes.  This class should not be directly instantiated.
48672  * @constructor
48673  */
48674 Roo.grid.AbstractSelectionModel = function(){
48675     this.locked = false;
48676     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48677 };
48678
48679 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48680     /** @ignore Called by the grid automatically. Do not call directly. */
48681     init : function(grid){
48682         this.grid = grid;
48683         this.initEvents();
48684     },
48685
48686     /**
48687      * Locks the selections.
48688      */
48689     lock : function(){
48690         this.locked = true;
48691     },
48692
48693     /**
48694      * Unlocks the selections.
48695      */
48696     unlock : function(){
48697         this.locked = false;
48698     },
48699
48700     /**
48701      * Returns true if the selections are locked.
48702      * @return {Boolean}
48703      */
48704     isLocked : function(){
48705         return this.locked;
48706     }
48707 });/*
48708  * Based on:
48709  * Ext JS Library 1.1.1
48710  * Copyright(c) 2006-2007, Ext JS, LLC.
48711  *
48712  * Originally Released Under LGPL - original licence link has changed is not relivant.
48713  *
48714  * Fork - LGPL
48715  * <script type="text/javascript">
48716  */
48717 /**
48718  * @extends Roo.grid.AbstractSelectionModel
48719  * @class Roo.grid.RowSelectionModel
48720  * The default SelectionModel used by {@link Roo.grid.Grid}.
48721  * It supports multiple selections and keyboard selection/navigation. 
48722  * @constructor
48723  * @param {Object} config
48724  */
48725 Roo.grid.RowSelectionModel = function(config){
48726     Roo.apply(this, config);
48727     this.selections = new Roo.util.MixedCollection(false, function(o){
48728         return o.id;
48729     });
48730
48731     this.last = false;
48732     this.lastActive = false;
48733
48734     this.addEvents({
48735         /**
48736              * @event selectionchange
48737              * Fires when the selection changes
48738              * @param {SelectionModel} this
48739              */
48740             "selectionchange" : true,
48741         /**
48742              * @event afterselectionchange
48743              * Fires after the selection changes (eg. by key press or clicking)
48744              * @param {SelectionModel} this
48745              */
48746             "afterselectionchange" : true,
48747         /**
48748              * @event beforerowselect
48749              * Fires when a row is selected being selected, return false to cancel.
48750              * @param {SelectionModel} this
48751              * @param {Number} rowIndex The selected index
48752              * @param {Boolean} keepExisting False if other selections will be cleared
48753              */
48754             "beforerowselect" : true,
48755         /**
48756              * @event rowselect
48757              * Fires when a row is selected.
48758              * @param {SelectionModel} this
48759              * @param {Number} rowIndex The selected index
48760              * @param {Roo.data.Record} r The record
48761              */
48762             "rowselect" : true,
48763         /**
48764              * @event rowdeselect
48765              * Fires when a row is deselected.
48766              * @param {SelectionModel} this
48767              * @param {Number} rowIndex The selected index
48768              */
48769         "rowdeselect" : true
48770     });
48771     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48772     this.locked = false;
48773 };
48774
48775 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48776     /**
48777      * @cfg {Boolean} singleSelect
48778      * True to allow selection of only one row at a time (defaults to false)
48779      */
48780     singleSelect : false,
48781
48782     // private
48783     initEvents : function(){
48784
48785         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48786             this.grid.on("mousedown", this.handleMouseDown, this);
48787         }else{ // allow click to work like normal
48788             this.grid.on("rowclick", this.handleDragableRowClick, this);
48789         }
48790
48791         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48792             "up" : function(e){
48793                 if(!e.shiftKey){
48794                     this.selectPrevious(e.shiftKey);
48795                 }else if(this.last !== false && this.lastActive !== false){
48796                     var last = this.last;
48797                     this.selectRange(this.last,  this.lastActive-1);
48798                     this.grid.getView().focusRow(this.lastActive);
48799                     if(last !== false){
48800                         this.last = last;
48801                     }
48802                 }else{
48803                     this.selectFirstRow();
48804                 }
48805                 this.fireEvent("afterselectionchange", this);
48806             },
48807             "down" : function(e){
48808                 if(!e.shiftKey){
48809                     this.selectNext(e.shiftKey);
48810                 }else if(this.last !== false && this.lastActive !== false){
48811                     var last = this.last;
48812                     this.selectRange(this.last,  this.lastActive+1);
48813                     this.grid.getView().focusRow(this.lastActive);
48814                     if(last !== false){
48815                         this.last = last;
48816                     }
48817                 }else{
48818                     this.selectFirstRow();
48819                 }
48820                 this.fireEvent("afterselectionchange", this);
48821             },
48822             scope: this
48823         });
48824
48825         var view = this.grid.view;
48826         view.on("refresh", this.onRefresh, this);
48827         view.on("rowupdated", this.onRowUpdated, this);
48828         view.on("rowremoved", this.onRemove, this);
48829     },
48830
48831     // private
48832     onRefresh : function(){
48833         var ds = this.grid.dataSource, i, v = this.grid.view;
48834         var s = this.selections;
48835         s.each(function(r){
48836             if((i = ds.indexOfId(r.id)) != -1){
48837                 v.onRowSelect(i);
48838             }else{
48839                 s.remove(r);
48840             }
48841         });
48842     },
48843
48844     // private
48845     onRemove : function(v, index, r){
48846         this.selections.remove(r);
48847     },
48848
48849     // private
48850     onRowUpdated : function(v, index, r){
48851         if(this.isSelected(r)){
48852             v.onRowSelect(index);
48853         }
48854     },
48855
48856     /**
48857      * Select records.
48858      * @param {Array} records The records to select
48859      * @param {Boolean} keepExisting (optional) True to keep existing selections
48860      */
48861     selectRecords : function(records, keepExisting){
48862         if(!keepExisting){
48863             this.clearSelections();
48864         }
48865         var ds = this.grid.dataSource;
48866         for(var i = 0, len = records.length; i < len; i++){
48867             this.selectRow(ds.indexOf(records[i]), true);
48868         }
48869     },
48870
48871     /**
48872      * Gets the number of selected rows.
48873      * @return {Number}
48874      */
48875     getCount : function(){
48876         return this.selections.length;
48877     },
48878
48879     /**
48880      * Selects the first row in the grid.
48881      */
48882     selectFirstRow : function(){
48883         this.selectRow(0);
48884     },
48885
48886     /**
48887      * Select the last row.
48888      * @param {Boolean} keepExisting (optional) True to keep existing selections
48889      */
48890     selectLastRow : function(keepExisting){
48891         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48892     },
48893
48894     /**
48895      * Selects the row immediately following the last selected row.
48896      * @param {Boolean} keepExisting (optional) True to keep existing selections
48897      */
48898     selectNext : function(keepExisting){
48899         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48900             this.selectRow(this.last+1, keepExisting);
48901             this.grid.getView().focusRow(this.last);
48902         }
48903     },
48904
48905     /**
48906      * Selects the row that precedes the last selected row.
48907      * @param {Boolean} keepExisting (optional) True to keep existing selections
48908      */
48909     selectPrevious : function(keepExisting){
48910         if(this.last){
48911             this.selectRow(this.last-1, keepExisting);
48912             this.grid.getView().focusRow(this.last);
48913         }
48914     },
48915
48916     /**
48917      * Returns the selected records
48918      * @return {Array} Array of selected records
48919      */
48920     getSelections : function(){
48921         return [].concat(this.selections.items);
48922     },
48923
48924     /**
48925      * Returns the first selected record.
48926      * @return {Record}
48927      */
48928     getSelected : function(){
48929         return this.selections.itemAt(0);
48930     },
48931
48932
48933     /**
48934      * Clears all selections.
48935      */
48936     clearSelections : function(fast){
48937         if(this.locked) return;
48938         if(fast !== true){
48939             var ds = this.grid.dataSource;
48940             var s = this.selections;
48941             s.each(function(r){
48942                 this.deselectRow(ds.indexOfId(r.id));
48943             }, this);
48944             s.clear();
48945         }else{
48946             this.selections.clear();
48947         }
48948         this.last = false;
48949     },
48950
48951
48952     /**
48953      * Selects all rows.
48954      */
48955     selectAll : function(){
48956         if(this.locked) return;
48957         this.selections.clear();
48958         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48959             this.selectRow(i, true);
48960         }
48961     },
48962
48963     /**
48964      * Returns True if there is a selection.
48965      * @return {Boolean}
48966      */
48967     hasSelection : function(){
48968         return this.selections.length > 0;
48969     },
48970
48971     /**
48972      * Returns True if the specified row is selected.
48973      * @param {Number/Record} record The record or index of the record to check
48974      * @return {Boolean}
48975      */
48976     isSelected : function(index){
48977         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48978         return (r && this.selections.key(r.id) ? true : false);
48979     },
48980
48981     /**
48982      * Returns True if the specified record id is selected.
48983      * @param {String} id The id of record to check
48984      * @return {Boolean}
48985      */
48986     isIdSelected : function(id){
48987         return (this.selections.key(id) ? true : false);
48988     },
48989
48990     // private
48991     handleMouseDown : function(e, t){
48992         var view = this.grid.getView(), rowIndex;
48993         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48994             return;
48995         };
48996         if(e.shiftKey && this.last !== false){
48997             var last = this.last;
48998             this.selectRange(last, rowIndex, e.ctrlKey);
48999             this.last = last; // reset the last
49000             view.focusRow(rowIndex);
49001         }else{
49002             var isSelected = this.isSelected(rowIndex);
49003             if(e.button !== 0 && isSelected){
49004                 view.focusRow(rowIndex);
49005             }else if(e.ctrlKey && isSelected){
49006                 this.deselectRow(rowIndex);
49007             }else if(!isSelected){
49008                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49009                 view.focusRow(rowIndex);
49010             }
49011         }
49012         this.fireEvent("afterselectionchange", this);
49013     },
49014     // private
49015     handleDragableRowClick :  function(grid, rowIndex, e) 
49016     {
49017         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49018             this.selectRow(rowIndex, false);
49019             grid.view.focusRow(rowIndex);
49020              this.fireEvent("afterselectionchange", this);
49021         }
49022     },
49023     
49024     /**
49025      * Selects multiple rows.
49026      * @param {Array} rows Array of the indexes of the row to select
49027      * @param {Boolean} keepExisting (optional) True to keep existing selections
49028      */
49029     selectRows : function(rows, keepExisting){
49030         if(!keepExisting){
49031             this.clearSelections();
49032         }
49033         for(var i = 0, len = rows.length; i < len; i++){
49034             this.selectRow(rows[i], true);
49035         }
49036     },
49037
49038     /**
49039      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49040      * @param {Number} startRow The index of the first row in the range
49041      * @param {Number} endRow The index of the last row in the range
49042      * @param {Boolean} keepExisting (optional) True to retain existing selections
49043      */
49044     selectRange : function(startRow, endRow, keepExisting){
49045         if(this.locked) return;
49046         if(!keepExisting){
49047             this.clearSelections();
49048         }
49049         if(startRow <= endRow){
49050             for(var i = startRow; i <= endRow; i++){
49051                 this.selectRow(i, true);
49052             }
49053         }else{
49054             for(var i = startRow; i >= endRow; i--){
49055                 this.selectRow(i, true);
49056             }
49057         }
49058     },
49059
49060     /**
49061      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49062      * @param {Number} startRow The index of the first row in the range
49063      * @param {Number} endRow The index of the last row in the range
49064      */
49065     deselectRange : function(startRow, endRow, preventViewNotify){
49066         if(this.locked) return;
49067         for(var i = startRow; i <= endRow; i++){
49068             this.deselectRow(i, preventViewNotify);
49069         }
49070     },
49071
49072     /**
49073      * Selects a row.
49074      * @param {Number} row The index of the row to select
49075      * @param {Boolean} keepExisting (optional) True to keep existing selections
49076      */
49077     selectRow : function(index, keepExisting, preventViewNotify){
49078         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49079         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49080             if(!keepExisting || this.singleSelect){
49081                 this.clearSelections();
49082             }
49083             var r = this.grid.dataSource.getAt(index);
49084             this.selections.add(r);
49085             this.last = this.lastActive = index;
49086             if(!preventViewNotify){
49087                 this.grid.getView().onRowSelect(index);
49088             }
49089             this.fireEvent("rowselect", this, index, r);
49090             this.fireEvent("selectionchange", this);
49091         }
49092     },
49093
49094     /**
49095      * Deselects a row.
49096      * @param {Number} row The index of the row to deselect
49097      */
49098     deselectRow : function(index, preventViewNotify){
49099         if(this.locked) return;
49100         if(this.last == index){
49101             this.last = false;
49102         }
49103         if(this.lastActive == index){
49104             this.lastActive = false;
49105         }
49106         var r = this.grid.dataSource.getAt(index);
49107         this.selections.remove(r);
49108         if(!preventViewNotify){
49109             this.grid.getView().onRowDeselect(index);
49110         }
49111         this.fireEvent("rowdeselect", this, index);
49112         this.fireEvent("selectionchange", this);
49113     },
49114
49115     // private
49116     restoreLast : function(){
49117         if(this._last){
49118             this.last = this._last;
49119         }
49120     },
49121
49122     // private
49123     acceptsNav : function(row, col, cm){
49124         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49125     },
49126
49127     // private
49128     onEditorKey : function(field, e){
49129         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49130         if(k == e.TAB){
49131             e.stopEvent();
49132             ed.completeEdit();
49133             if(e.shiftKey){
49134                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49135             }else{
49136                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49137             }
49138         }else if(k == e.ENTER && !e.ctrlKey){
49139             e.stopEvent();
49140             ed.completeEdit();
49141             if(e.shiftKey){
49142                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49143             }else{
49144                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49145             }
49146         }else if(k == e.ESC){
49147             ed.cancelEdit();
49148         }
49149         if(newCell){
49150             g.startEditing(newCell[0], newCell[1]);
49151         }
49152     }
49153 });/*
49154  * Based on:
49155  * Ext JS Library 1.1.1
49156  * Copyright(c) 2006-2007, Ext JS, LLC.
49157  *
49158  * Originally Released Under LGPL - original licence link has changed is not relivant.
49159  *
49160  * Fork - LGPL
49161  * <script type="text/javascript">
49162  */
49163 /**
49164  * @class Roo.grid.CellSelectionModel
49165  * @extends Roo.grid.AbstractSelectionModel
49166  * This class provides the basic implementation for cell selection in a grid.
49167  * @constructor
49168  * @param {Object} config The object containing the configuration of this model.
49169  */
49170 Roo.grid.CellSelectionModel = function(config){
49171     Roo.apply(this, config);
49172
49173     this.selection = null;
49174
49175     this.addEvents({
49176         /**
49177              * @event beforerowselect
49178              * Fires before a cell is selected.
49179              * @param {SelectionModel} this
49180              * @param {Number} rowIndex The selected row index
49181              * @param {Number} colIndex The selected cell index
49182              */
49183             "beforecellselect" : true,
49184         /**
49185              * @event cellselect
49186              * Fires when a cell is selected.
49187              * @param {SelectionModel} this
49188              * @param {Number} rowIndex The selected row index
49189              * @param {Number} colIndex The selected cell index
49190              */
49191             "cellselect" : true,
49192         /**
49193              * @event selectionchange
49194              * Fires when the active selection changes.
49195              * @param {SelectionModel} this
49196              * @param {Object} selection null for no selection or an object (o) with two properties
49197                 <ul>
49198                 <li>o.record: the record object for the row the selection is in</li>
49199                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49200                 </ul>
49201              */
49202             "selectionchange" : true
49203     });
49204     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49205 };
49206
49207 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49208
49209     /** @ignore */
49210     initEvents : function(){
49211         this.grid.on("mousedown", this.handleMouseDown, this);
49212         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49213         var view = this.grid.view;
49214         view.on("refresh", this.onViewChange, this);
49215         view.on("rowupdated", this.onRowUpdated, this);
49216         view.on("beforerowremoved", this.clearSelections, this);
49217         view.on("beforerowsinserted", this.clearSelections, this);
49218         if(this.grid.isEditor){
49219             this.grid.on("beforeedit", this.beforeEdit,  this);
49220         }
49221     },
49222
49223         //private
49224     beforeEdit : function(e){
49225         this.select(e.row, e.column, false, true, e.record);
49226     },
49227
49228         //private
49229     onRowUpdated : function(v, index, r){
49230         if(this.selection && this.selection.record == r){
49231             v.onCellSelect(index, this.selection.cell[1]);
49232         }
49233     },
49234
49235         //private
49236     onViewChange : function(){
49237         this.clearSelections(true);
49238     },
49239
49240         /**
49241          * Returns the currently selected cell,.
49242          * @return {Array} The selected cell (row, column) or null if none selected.
49243          */
49244     getSelectedCell : function(){
49245         return this.selection ? this.selection.cell : null;
49246     },
49247
49248     /**
49249      * Clears all selections.
49250      * @param {Boolean} true to prevent the gridview from being notified about the change.
49251      */
49252     clearSelections : function(preventNotify){
49253         var s = this.selection;
49254         if(s){
49255             if(preventNotify !== true){
49256                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49257             }
49258             this.selection = null;
49259             this.fireEvent("selectionchange", this, null);
49260         }
49261     },
49262
49263     /**
49264      * Returns true if there is a selection.
49265      * @return {Boolean}
49266      */
49267     hasSelection : function(){
49268         return this.selection ? true : false;
49269     },
49270
49271     /** @ignore */
49272     handleMouseDown : function(e, t){
49273         var v = this.grid.getView();
49274         if(this.isLocked()){
49275             return;
49276         };
49277         var row = v.findRowIndex(t);
49278         var cell = v.findCellIndex(t);
49279         if(row !== false && cell !== false){
49280             this.select(row, cell);
49281         }
49282     },
49283
49284     /**
49285      * Selects a cell.
49286      * @param {Number} rowIndex
49287      * @param {Number} collIndex
49288      */
49289     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49290         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49291             this.clearSelections();
49292             r = r || this.grid.dataSource.getAt(rowIndex);
49293             this.selection = {
49294                 record : r,
49295                 cell : [rowIndex, colIndex]
49296             };
49297             if(!preventViewNotify){
49298                 var v = this.grid.getView();
49299                 v.onCellSelect(rowIndex, colIndex);
49300                 if(preventFocus !== true){
49301                     v.focusCell(rowIndex, colIndex);
49302                 }
49303             }
49304             this.fireEvent("cellselect", this, rowIndex, colIndex);
49305             this.fireEvent("selectionchange", this, this.selection);
49306         }
49307     },
49308
49309         //private
49310     isSelectable : function(rowIndex, colIndex, cm){
49311         return !cm.isHidden(colIndex);
49312     },
49313
49314     /** @ignore */
49315     handleKeyDown : function(e){
49316         if(!e.isNavKeyPress()){
49317             return;
49318         }
49319         var g = this.grid, s = this.selection;
49320         if(!s){
49321             e.stopEvent();
49322             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49323             if(cell){
49324                 this.select(cell[0], cell[1]);
49325             }
49326             return;
49327         }
49328         var sm = this;
49329         var walk = function(row, col, step){
49330             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49331         };
49332         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49333         var newCell;
49334
49335         switch(k){
49336              case e.TAB:
49337                  if(e.shiftKey){
49338                      newCell = walk(r, c-1, -1);
49339                  }else{
49340                      newCell = walk(r, c+1, 1);
49341                  }
49342              break;
49343              case e.DOWN:
49344                  newCell = walk(r+1, c, 1);
49345              break;
49346              case e.UP:
49347                  newCell = walk(r-1, c, -1);
49348              break;
49349              case e.RIGHT:
49350                  newCell = walk(r, c+1, 1);
49351              break;
49352              case e.LEFT:
49353                  newCell = walk(r, c-1, -1);
49354              break;
49355              case e.ENTER:
49356                  if(g.isEditor && !g.editing){
49357                     g.startEditing(r, c);
49358                     e.stopEvent();
49359                     return;
49360                 }
49361              break;
49362         };
49363         if(newCell){
49364             this.select(newCell[0], newCell[1]);
49365             e.stopEvent();
49366         }
49367     },
49368
49369     acceptsNav : function(row, col, cm){
49370         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49371     },
49372
49373     onEditorKey : function(field, e){
49374         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49375         if(k == e.TAB){
49376             if(e.shiftKey){
49377                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49378             }else{
49379                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49380             }
49381             e.stopEvent();
49382         }else if(k == e.ENTER && !e.ctrlKey){
49383             ed.completeEdit();
49384             e.stopEvent();
49385         }else if(k == e.ESC){
49386             ed.cancelEdit();
49387         }
49388         if(newCell){
49389             g.startEditing(newCell[0], newCell[1]);
49390         }
49391     }
49392 });/*
49393  * Based on:
49394  * Ext JS Library 1.1.1
49395  * Copyright(c) 2006-2007, Ext JS, LLC.
49396  *
49397  * Originally Released Under LGPL - original licence link has changed is not relivant.
49398  *
49399  * Fork - LGPL
49400  * <script type="text/javascript">
49401  */
49402  
49403 /**
49404  * @class Roo.grid.EditorGrid
49405  * @extends Roo.grid.Grid
49406  * Class for creating and editable grid.
49407  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49408  * The container MUST have some type of size defined for the grid to fill. The container will be 
49409  * automatically set to position relative if it isn't already.
49410  * @param {Object} dataSource The data model to bind to
49411  * @param {Object} colModel The column model with info about this grid's columns
49412  */
49413 Roo.grid.EditorGrid = function(container, config){
49414     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49415     this.getGridEl().addClass("xedit-grid");
49416
49417     if(!this.selModel){
49418         this.selModel = new Roo.grid.CellSelectionModel();
49419     }
49420
49421     this.activeEditor = null;
49422
49423         this.addEvents({
49424             /**
49425              * @event beforeedit
49426              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49427              * <ul style="padding:5px;padding-left:16px;">
49428              * <li>grid - This grid</li>
49429              * <li>record - The record being edited</li>
49430              * <li>field - The field name being edited</li>
49431              * <li>value - The value for the field being edited.</li>
49432              * <li>row - The grid row index</li>
49433              * <li>column - The grid column index</li>
49434              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49435              * </ul>
49436              * @param {Object} e An edit event (see above for description)
49437              */
49438             "beforeedit" : true,
49439             /**
49440              * @event afteredit
49441              * Fires after a cell is edited. <br />
49442              * <ul style="padding:5px;padding-left:16px;">
49443              * <li>grid - This grid</li>
49444              * <li>record - The record being edited</li>
49445              * <li>field - The field name being edited</li>
49446              * <li>value - The value being set</li>
49447              * <li>originalValue - The original value for the field, before the edit.</li>
49448              * <li>row - The grid row index</li>
49449              * <li>column - The grid column index</li>
49450              * </ul>
49451              * @param {Object} e An edit event (see above for description)
49452              */
49453             "afteredit" : true,
49454             /**
49455              * @event validateedit
49456              * Fires after a cell is edited, but before the value is set in the record. 
49457          * You can use this to modify the value being set in the field, Return false
49458              * to cancel the change. The edit event object has the following properties <br />
49459              * <ul style="padding:5px;padding-left:16px;">
49460          * <li>editor - This editor</li>
49461              * <li>grid - This grid</li>
49462              * <li>record - The record being edited</li>
49463              * <li>field - The field name being edited</li>
49464              * <li>value - The value being set</li>
49465              * <li>originalValue - The original value for the field, before the edit.</li>
49466              * <li>row - The grid row index</li>
49467              * <li>column - The grid column index</li>
49468              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49469              * </ul>
49470              * @param {Object} e An edit event (see above for description)
49471              */
49472             "validateedit" : true
49473         });
49474     this.on("bodyscroll", this.stopEditing,  this);
49475     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49476 };
49477
49478 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49479     /**
49480      * @cfg {Number} clicksToEdit
49481      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49482      */
49483     clicksToEdit: 2,
49484
49485     // private
49486     isEditor : true,
49487     // private
49488     trackMouseOver: false, // causes very odd FF errors
49489
49490     onCellDblClick : function(g, row, col){
49491         this.startEditing(row, col);
49492     },
49493
49494     onEditComplete : function(ed, value, startValue){
49495         this.editing = false;
49496         this.activeEditor = null;
49497         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49498         var r = ed.record;
49499         var field = this.colModel.getDataIndex(ed.col);
49500         var e = {
49501             grid: this,
49502             record: r,
49503             field: field,
49504             originalValue: startValue,
49505             value: value,
49506             row: ed.row,
49507             column: ed.col,
49508             cancel:false,
49509             editor: ed
49510         };
49511         if(String(value) !== String(startValue)){
49512             
49513             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49514                 r.set(field, e.value);
49515                 delete e.cancel; //?? why!!!
49516                 this.fireEvent("afteredit", e);
49517             }
49518         } else {
49519             this.fireEvent("afteredit", e); // always fir it!
49520         }
49521         this.view.focusCell(ed.row, ed.col);
49522     },
49523
49524     /**
49525      * Starts editing the specified for the specified row/column
49526      * @param {Number} rowIndex
49527      * @param {Number} colIndex
49528      */
49529     startEditing : function(row, col){
49530         this.stopEditing();
49531         if(this.colModel.isCellEditable(col, row)){
49532             this.view.ensureVisible(row, col, true);
49533             var r = this.dataSource.getAt(row);
49534             var field = this.colModel.getDataIndex(col);
49535             var e = {
49536                 grid: this,
49537                 record: r,
49538                 field: field,
49539                 value: r.data[field],
49540                 row: row,
49541                 column: col,
49542                 cancel:false
49543             };
49544             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49545                 this.editing = true;
49546                 var ed = this.colModel.getCellEditor(col, row);
49547                 
49548                 if (!ed) {
49549                     return;
49550                 }
49551                 if(!ed.rendered){
49552                     ed.render(ed.parentEl || document.body);
49553                 }
49554                 ed.field.reset();
49555                 (function(){ // complex but required for focus issues in safari, ie and opera
49556                     ed.row = row;
49557                     ed.col = col;
49558                     ed.record = r;
49559                     ed.on("complete", this.onEditComplete, this, {single: true});
49560                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49561                     this.activeEditor = ed;
49562                     var v = r.data[field];
49563                     ed.startEdit(this.view.getCell(row, col), v);
49564                 }).defer(50, this);
49565             }
49566         }
49567     },
49568         
49569     /**
49570      * Stops any active editing
49571      */
49572     stopEditing : function(){
49573         if(this.activeEditor){
49574             this.activeEditor.completeEdit();
49575         }
49576         this.activeEditor = null;
49577     }
49578 });/*
49579  * Based on:
49580  * Ext JS Library 1.1.1
49581  * Copyright(c) 2006-2007, Ext JS, LLC.
49582  *
49583  * Originally Released Under LGPL - original licence link has changed is not relivant.
49584  *
49585  * Fork - LGPL
49586  * <script type="text/javascript">
49587  */
49588
49589 // private - not really -- you end up using it !
49590 // This is a support class used internally by the Grid components
49591
49592 /**
49593  * @class Roo.grid.GridEditor
49594  * @extends Roo.Editor
49595  * Class for creating and editable grid elements.
49596  * @param {Object} config any settings (must include field)
49597  */
49598 Roo.grid.GridEditor = function(field, config){
49599     if (!config && field.field) {
49600         config = field;
49601         field = Roo.factory(config.field, Roo.form);
49602     }
49603     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49604     field.monitorTab = false;
49605 };
49606
49607 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49608     
49609     /**
49610      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49611      */
49612     
49613     alignment: "tl-tl",
49614     autoSize: "width",
49615     hideEl : false,
49616     cls: "x-small-editor x-grid-editor",
49617     shim:false,
49618     shadow:"frame"
49619 });/*
49620  * Based on:
49621  * Ext JS Library 1.1.1
49622  * Copyright(c) 2006-2007, Ext JS, LLC.
49623  *
49624  * Originally Released Under LGPL - original licence link has changed is not relivant.
49625  *
49626  * Fork - LGPL
49627  * <script type="text/javascript">
49628  */
49629   
49630
49631   
49632 Roo.grid.PropertyRecord = Roo.data.Record.create([
49633     {name:'name',type:'string'},  'value'
49634 ]);
49635
49636
49637 Roo.grid.PropertyStore = function(grid, source){
49638     this.grid = grid;
49639     this.store = new Roo.data.Store({
49640         recordType : Roo.grid.PropertyRecord
49641     });
49642     this.store.on('update', this.onUpdate,  this);
49643     if(source){
49644         this.setSource(source);
49645     }
49646     Roo.grid.PropertyStore.superclass.constructor.call(this);
49647 };
49648
49649
49650
49651 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49652     setSource : function(o){
49653         this.source = o;
49654         this.store.removeAll();
49655         var data = [];
49656         for(var k in o){
49657             if(this.isEditableValue(o[k])){
49658                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49659             }
49660         }
49661         this.store.loadRecords({records: data}, {}, true);
49662     },
49663
49664     onUpdate : function(ds, record, type){
49665         if(type == Roo.data.Record.EDIT){
49666             var v = record.data['value'];
49667             var oldValue = record.modified['value'];
49668             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49669                 this.source[record.id] = v;
49670                 record.commit();
49671                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49672             }else{
49673                 record.reject();
49674             }
49675         }
49676     },
49677
49678     getProperty : function(row){
49679        return this.store.getAt(row);
49680     },
49681
49682     isEditableValue: function(val){
49683         if(val && val instanceof Date){
49684             return true;
49685         }else if(typeof val == 'object' || typeof val == 'function'){
49686             return false;
49687         }
49688         return true;
49689     },
49690
49691     setValue : function(prop, value){
49692         this.source[prop] = value;
49693         this.store.getById(prop).set('value', value);
49694     },
49695
49696     getSource : function(){
49697         return this.source;
49698     }
49699 });
49700
49701 Roo.grid.PropertyColumnModel = function(grid, store){
49702     this.grid = grid;
49703     var g = Roo.grid;
49704     g.PropertyColumnModel.superclass.constructor.call(this, [
49705         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49706         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49707     ]);
49708     this.store = store;
49709     this.bselect = Roo.DomHelper.append(document.body, {
49710         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49711             {tag: 'option', value: 'true', html: 'true'},
49712             {tag: 'option', value: 'false', html: 'false'}
49713         ]
49714     });
49715     Roo.id(this.bselect);
49716     var f = Roo.form;
49717     this.editors = {
49718         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49719         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49720         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49721         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49722         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49723     };
49724     this.renderCellDelegate = this.renderCell.createDelegate(this);
49725     this.renderPropDelegate = this.renderProp.createDelegate(this);
49726 };
49727
49728 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49729     
49730     
49731     nameText : 'Name',
49732     valueText : 'Value',
49733     
49734     dateFormat : 'm/j/Y',
49735     
49736     
49737     renderDate : function(dateVal){
49738         return dateVal.dateFormat(this.dateFormat);
49739     },
49740
49741     renderBool : function(bVal){
49742         return bVal ? 'true' : 'false';
49743     },
49744
49745     isCellEditable : function(colIndex, rowIndex){
49746         return colIndex == 1;
49747     },
49748
49749     getRenderer : function(col){
49750         return col == 1 ?
49751             this.renderCellDelegate : this.renderPropDelegate;
49752     },
49753
49754     renderProp : function(v){
49755         return this.getPropertyName(v);
49756     },
49757
49758     renderCell : function(val){
49759         var rv = val;
49760         if(val instanceof Date){
49761             rv = this.renderDate(val);
49762         }else if(typeof val == 'boolean'){
49763             rv = this.renderBool(val);
49764         }
49765         return Roo.util.Format.htmlEncode(rv);
49766     },
49767
49768     getPropertyName : function(name){
49769         var pn = this.grid.propertyNames;
49770         return pn && pn[name] ? pn[name] : name;
49771     },
49772
49773     getCellEditor : function(colIndex, rowIndex){
49774         var p = this.store.getProperty(rowIndex);
49775         var n = p.data['name'], val = p.data['value'];
49776         
49777         if(typeof(this.grid.customEditors[n]) == 'string'){
49778             return this.editors[this.grid.customEditors[n]];
49779         }
49780         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49781             return this.grid.customEditors[n];
49782         }
49783         if(val instanceof Date){
49784             return this.editors['date'];
49785         }else if(typeof val == 'number'){
49786             return this.editors['number'];
49787         }else if(typeof val == 'boolean'){
49788             return this.editors['boolean'];
49789         }else{
49790             return this.editors['string'];
49791         }
49792     }
49793 });
49794
49795 /**
49796  * @class Roo.grid.PropertyGrid
49797  * @extends Roo.grid.EditorGrid
49798  * This class represents the  interface of a component based property grid control.
49799  * <br><br>Usage:<pre><code>
49800  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49801       
49802  });
49803  // set any options
49804  grid.render();
49805  * </code></pre>
49806   
49807  * @constructor
49808  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49809  * The container MUST have some type of size defined for the grid to fill. The container will be
49810  * automatically set to position relative if it isn't already.
49811  * @param {Object} config A config object that sets properties on this grid.
49812  */
49813 Roo.grid.PropertyGrid = function(container, config){
49814     config = config || {};
49815     var store = new Roo.grid.PropertyStore(this);
49816     this.store = store;
49817     var cm = new Roo.grid.PropertyColumnModel(this, store);
49818     store.store.sort('name', 'ASC');
49819     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49820         ds: store.store,
49821         cm: cm,
49822         enableColLock:false,
49823         enableColumnMove:false,
49824         stripeRows:false,
49825         trackMouseOver: false,
49826         clicksToEdit:1
49827     }, config));
49828     this.getGridEl().addClass('x-props-grid');
49829     this.lastEditRow = null;
49830     this.on('columnresize', this.onColumnResize, this);
49831     this.addEvents({
49832          /**
49833              * @event beforepropertychange
49834              * Fires before a property changes (return false to stop?)
49835              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49836              * @param {String} id Record Id
49837              * @param {String} newval New Value
49838          * @param {String} oldval Old Value
49839              */
49840         "beforepropertychange": true,
49841         /**
49842              * @event propertychange
49843              * Fires after a property changes
49844              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49845              * @param {String} id Record Id
49846              * @param {String} newval New Value
49847          * @param {String} oldval Old Value
49848              */
49849         "propertychange": true
49850     });
49851     this.customEditors = this.customEditors || {};
49852 };
49853 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49854     
49855      /**
49856      * @cfg {Object} customEditors map of colnames=> custom editors.
49857      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49858      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49859      * false disables editing of the field.
49860          */
49861     
49862       /**
49863      * @cfg {Object} propertyNames map of property Names to their displayed value
49864          */
49865     
49866     render : function(){
49867         Roo.grid.PropertyGrid.superclass.render.call(this);
49868         this.autoSize.defer(100, this);
49869     },
49870
49871     autoSize : function(){
49872         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49873         if(this.view){
49874             this.view.fitColumns();
49875         }
49876     },
49877
49878     onColumnResize : function(){
49879         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49880         this.autoSize();
49881     },
49882     /**
49883      * Sets the data for the Grid
49884      * accepts a Key => Value object of all the elements avaiable.
49885      * @param {Object} data  to appear in grid.
49886      */
49887     setSource : function(source){
49888         this.store.setSource(source);
49889         //this.autoSize();
49890     },
49891     /**
49892      * Gets all the data from the grid.
49893      * @return {Object} data  data stored in grid
49894      */
49895     getSource : function(){
49896         return this.store.getSource();
49897     }
49898 });/*
49899  * Based on:
49900  * Ext JS Library 1.1.1
49901  * Copyright(c) 2006-2007, Ext JS, LLC.
49902  *
49903  * Originally Released Under LGPL - original licence link has changed is not relivant.
49904  *
49905  * Fork - LGPL
49906  * <script type="text/javascript">
49907  */
49908  
49909 /**
49910  * @class Roo.LoadMask
49911  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49912  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49913  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49914  * element's UpdateManager load indicator and will be destroyed after the initial load.
49915  * @constructor
49916  * Create a new LoadMask
49917  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49918  * @param {Object} config The config object
49919  */
49920 Roo.LoadMask = function(el, config){
49921     this.el = Roo.get(el);
49922     Roo.apply(this, config);
49923     if(this.store){
49924         this.store.on('beforeload', this.onBeforeLoad, this);
49925         this.store.on('load', this.onLoad, this);
49926         this.store.on('loadexception', this.onLoad, this);
49927         this.removeMask = false;
49928     }else{
49929         var um = this.el.getUpdateManager();
49930         um.showLoadIndicator = false; // disable the default indicator
49931         um.on('beforeupdate', this.onBeforeLoad, this);
49932         um.on('update', this.onLoad, this);
49933         um.on('failure', this.onLoad, this);
49934         this.removeMask = true;
49935     }
49936 };
49937
49938 Roo.LoadMask.prototype = {
49939     /**
49940      * @cfg {Boolean} removeMask
49941      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49942      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49943      */
49944     /**
49945      * @cfg {String} msg
49946      * The text to display in a centered loading message box (defaults to 'Loading...')
49947      */
49948     msg : 'Loading...',
49949     /**
49950      * @cfg {String} msgCls
49951      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49952      */
49953     msgCls : 'x-mask-loading',
49954
49955     /**
49956      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49957      * @type Boolean
49958      */
49959     disabled: false,
49960
49961     /**
49962      * Disables the mask to prevent it from being displayed
49963      */
49964     disable : function(){
49965        this.disabled = true;
49966     },
49967
49968     /**
49969      * Enables the mask so that it can be displayed
49970      */
49971     enable : function(){
49972         this.disabled = false;
49973     },
49974
49975     // private
49976     onLoad : function(){
49977         this.el.unmask(this.removeMask);
49978     },
49979
49980     // private
49981     onBeforeLoad : function(){
49982         if(!this.disabled){
49983             this.el.mask(this.msg, this.msgCls);
49984         }
49985     },
49986
49987     // private
49988     destroy : function(){
49989         if(this.store){
49990             this.store.un('beforeload', this.onBeforeLoad, this);
49991             this.store.un('load', this.onLoad, this);
49992             this.store.un('loadexception', this.onLoad, this);
49993         }else{
49994             var um = this.el.getUpdateManager();
49995             um.un('beforeupdate', this.onBeforeLoad, this);
49996             um.un('update', this.onLoad, this);
49997             um.un('failure', this.onLoad, this);
49998         }
49999     }
50000 };/*
50001  * Based on:
50002  * Ext JS Library 1.1.1
50003  * Copyright(c) 2006-2007, Ext JS, LLC.
50004  *
50005  * Originally Released Under LGPL - original licence link has changed is not relivant.
50006  *
50007  * Fork - LGPL
50008  * <script type="text/javascript">
50009  */
50010 Roo.XTemplate = function(){
50011     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50012     var s = this.html;
50013
50014     s = ['<tpl>', s, '</tpl>'].join('');
50015
50016     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50017
50018     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50019     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50020     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50021     var m, id = 0;
50022     var tpls = [];
50023
50024     while(m = s.match(re)){
50025        var m2 = m[0].match(nameRe);
50026        var m3 = m[0].match(ifRe);
50027        var m4 = m[0].match(execRe);
50028        var exp = null, fn = null, exec = null;
50029        var name = m2 && m2[1] ? m2[1] : '';
50030        if(m3){
50031            exp = m3 && m3[1] ? m3[1] : null;
50032            if(exp){
50033                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50034            }
50035        }
50036        if(m4){
50037            exp = m4 && m4[1] ? m4[1] : null;
50038            if(exp){
50039                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50040            }
50041        }
50042        if(name){
50043            switch(name){
50044                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50045                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50046                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50047            }
50048        }
50049        tpls.push({
50050             id: id,
50051             target: name,
50052             exec: exec,
50053             test: fn,
50054             body: m[1]||''
50055         });
50056        s = s.replace(m[0], '{xtpl'+ id + '}');
50057        ++id;
50058     }
50059     for(var i = tpls.length-1; i >= 0; --i){
50060         this.compileTpl(tpls[i]);
50061     }
50062     this.master = tpls[tpls.length-1];
50063     this.tpls = tpls;
50064 };
50065 Roo.extend(Roo.XTemplate, Roo.Template, {
50066
50067     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50068
50069     applySubTemplate : function(id, values, parent){
50070         var t = this.tpls[id];
50071         if(t.test && !t.test.call(this, values, parent)){
50072             return '';
50073         }
50074         if(t.exec && t.exec.call(this, values, parent)){
50075             return '';
50076         }
50077         var vs = t.target ? t.target.call(this, values, parent) : values;
50078         parent = t.target ? values : parent;
50079         if(t.target && vs instanceof Array){
50080             var buf = [];
50081             for(var i = 0, len = vs.length; i < len; i++){
50082                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50083             }
50084             return buf.join('');
50085         }
50086         return t.compiled.call(this, vs, parent);
50087     },
50088
50089     compileTpl : function(tpl){
50090         var fm = Roo.util.Format;
50091         var useF = this.disableFormats !== true;
50092         var sep = Roo.isGecko ? "+" : ",";
50093         var fn = function(m, name, format, args){
50094             if(name.substr(0, 4) == 'xtpl'){
50095                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50096             }
50097             var v;
50098             if(name.indexOf('.') != -1){
50099                 v = name;
50100             }else{
50101                 v = "values['" + name + "']";
50102             }
50103             if(format && useF){
50104                 args = args ? ',' + args : "";
50105                 if(format.substr(0, 5) != "this."){
50106                     format = "fm." + format + '(';
50107                 }else{
50108                     format = 'this.call("'+ format.substr(5) + '", ';
50109                     args = ", values";
50110                 }
50111             }else{
50112                 args= ''; format = "("+v+" === undefined ? '' : ";
50113             }
50114             return "'"+ sep + format + v + args + ")"+sep+"'";
50115         };
50116         var body;
50117         // branched to use + in gecko and [].join() in others
50118         if(Roo.isGecko){
50119             body = "tpl.compiled = function(values, parent){ return '" +
50120                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50121                     "';};";
50122         }else{
50123             body = ["tpl.compiled = function(values, parent){ return ['"];
50124             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50125             body.push("'].join('');};");
50126             body = body.join('');
50127         }
50128         /** eval:var:zzzzzzz */
50129         eval(body);
50130         return this;
50131     },
50132
50133     applyTemplate : function(values){
50134         return this.master.compiled.call(this, values, {});
50135         var s = this.subs;
50136     },
50137
50138     apply : function(){
50139         return this.applyTemplate.apply(this, arguments);
50140     },
50141
50142     compile : function(){return this;}
50143 });
50144
50145 Roo.XTemplate.from = function(el){
50146     el = Roo.getDom(el);
50147     return new Roo.XTemplate(el.value || el.innerHTML);
50148 };/*
50149  * Original code for Roojs - LGPL
50150  * <script type="text/javascript">
50151  */
50152  
50153 /**
50154  * @class Roo.XComponent
50155  * A delayed Element creator...
50156  * 
50157  * Mypart.xyx = new Roo.XComponent({
50158
50159     parent : 'Mypart.xyz', // empty == document.element.!!
50160     order : '001',
50161     name : 'xxxx'
50162     region : 'xxxx'
50163     disabled : function() {} 
50164      
50165     tree : function() { // return an tree of xtype declared components
50166         var MODULE = this;
50167         return 
50168         {
50169             xtype : 'NestedLayoutPanel',
50170             // technicall
50171         }
50172      ]
50173  *})
50174  * @extends Roo.util.Observable
50175  * @constructor
50176  * @param cfg {Object} configuration of component
50177  * 
50178  */
50179 Roo.XComponent = function(cfg) {
50180     Roo.apply(this, cfg);
50181     this.addEvents({ 
50182         /**
50183              * @event built
50184              * Fires when this the componnt is built
50185              * @param {Roo.XComponent} c the component
50186              */
50187         'built' : true,
50188         /**
50189              * @event buildcomplete
50190              * Fires on the top level element when all elements have been built
50191              * @param {Roo.XComponent} c the top level component.
50192          */
50193         'buildcomplete' : true
50194         
50195     });
50196     
50197     Roo.XComponent.register(this);
50198     this.modules = false;
50199     this.el = false; // where the layout goes..
50200     
50201     
50202 }
50203 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50204     /**
50205      * @property el
50206      * The created element (with Roo.factory())
50207      * @type {Roo.Layout}
50208      */
50209     el  : false,
50210     
50211     /**
50212      * @property el
50213      * for BC  - use el in new code
50214      * @type {Roo.Layout}
50215      */
50216     panel : false,
50217     
50218     /**
50219      * @property layout
50220      * for BC  - use el in new code
50221      * @type {Roo.Layout}
50222      */
50223     layout : false,
50224     
50225      /**
50226      * @cfg {Function|boolean} disabled
50227      * If this module is disabled by some rule, return true from the funtion
50228      */
50229     disabled : false,
50230     
50231     /**
50232      * @cfg {String} parent 
50233      * Name of parent element which it get xtype added to..
50234      */
50235     parent: false,
50236     
50237     /**
50238      * @cfg {String} order
50239      * Used to set the order in which elements are created (usefull for multiple tabs)
50240      */
50241     
50242     order : false,
50243     /**
50244      * @cfg {String} name
50245      * String to display while loading.
50246      */
50247     name : false,
50248     /**
50249      * @cfg {Array} items
50250      * A single item array - the first element is the root of the tree..
50251      * It's done this way to stay compatible with the Xtype system...
50252      */
50253     items : false
50254      
50255      
50256     
50257 });
50258
50259 Roo.apply(Roo.XComponent, {
50260     
50261     /**
50262      * @property  buildCompleted
50263      * True when the builder has completed building the interface.
50264      * @type Boolean
50265      */
50266     buildCompleted : false,
50267      
50268     /**
50269      * @property  topModule
50270      * the upper most module - uses document.element as it's constructor.
50271      * @type Object
50272      */
50273      
50274     topModule  : false,
50275       
50276     /**
50277      * @property  modules
50278      * array of modules to be created by registration system.
50279      * @type Roo.XComponent
50280      */
50281     
50282     modules : [],
50283       
50284     
50285     /**
50286      * Register components to be built later.
50287      *
50288      * This solves the following issues
50289      * - Building is not done on page load, but after an authentication process has occured.
50290      * - Interface elements are registered on page load
50291      * - Parent Interface elements may not be loaded before child, so this handles that..
50292      * 
50293      *
50294      * example:
50295      * 
50296      * MyApp.register({
50297           order : '000001',
50298           module : 'Pman.Tab.projectMgr',
50299           region : 'center',
50300           parent : 'Pman.layout',
50301           disabled : false,  // or use a function..
50302         })
50303      
50304      * * @param {Object} details about module
50305      */
50306     register : function(obj) {
50307         this.modules.push(obj);
50308          
50309     },
50310     /**
50311      * convert a string to an object..
50312      * 
50313      */
50314     
50315     toObject : function(str)
50316     {
50317         if (!str || typeof(str) == 'object') {
50318             return str;
50319         }
50320         var ar = str.split('.');
50321         var rt, o;
50322         rt = ar.shift();
50323             /** eval:var:o */
50324         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50325         if (o === false) {
50326             throw "Module not found : " + str;
50327         }
50328         Roo.each(ar, function(e) {
50329             if (typeof(o[e]) == 'undefined') {
50330                 throw "Module not found : " + str;
50331             }
50332             o = o[e];
50333         });
50334         return o;
50335         
50336     },
50337     
50338     
50339     /**
50340      * move modules into their correct place in the tree..
50341      * 
50342      */
50343     preBuild : function ()
50344     {
50345         
50346         Roo.each(this.modules , function (obj)
50347         {
50348             obj.parent = this.toObject(obj.parent);
50349             
50350             if (!obj.parent) {
50351                 this.topModule = obj;
50352                 return;
50353             }
50354             
50355             if (!obj.parent.modules) {
50356                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50357                     function(o) { return o.order + '' }
50358                 );
50359             }
50360             
50361             obj.parent.modules.add(obj);
50362         }, this);
50363     },
50364     
50365      /**
50366      * make a list of modules to build.
50367      * @return {Array} list of modules. 
50368      */ 
50369     
50370     buildOrder : function()
50371     {
50372         var _this = this;
50373         var cmp = function(a,b) {   
50374             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50375         };
50376         
50377         if (!this.topModule || !this.topModule.modules) {
50378             throw "No top level modules to build";
50379         }
50380        
50381         // make a flat list in order of modules to build.
50382         var mods = [ this.topModule ];
50383         
50384         
50385         // add modules to their parents..
50386         var addMod = function(m) {
50387            // Roo.debug && Roo.log(m.modKey);
50388             
50389             mods.push(m);
50390             if (m.modules) {
50391                 m.modules.keySort('ASC',  cmp );
50392                 m.modules.each(addMod);
50393             }
50394             // not sure if this is used any more..
50395             if (m.finalize) {
50396                 m.finalize.name = m.name + " (clean up) ";
50397                 mods.push(m.finalize);
50398             }
50399             
50400         }
50401         this.topModule.modules.keySort('ASC',  cmp );
50402         this.topModule.modules.each(addMod);
50403         return mods;
50404     },
50405     
50406      /**
50407      * Build the registered modules.
50408      * @param {Object} parent element.
50409      * @param {Function} optional method to call after module has been added.
50410      * 
50411      */ 
50412    
50413     build : function() 
50414     {
50415         
50416         this.preBuild();
50417         var mods = this.buildOrder();
50418       
50419         //this.allmods = mods;
50420         //Roo.debug && Roo.log(mods);
50421         //return;
50422         if (!mods.length) { // should not happen
50423             throw "NO modules!!!";
50424         }
50425         
50426         
50427         
50428         // flash it up as modal - so we store the mask!?
50429         Roo.MessageBox.show({ title: 'loading' });
50430         Roo.MessageBox.show({
50431            title: "Please wait...",
50432            msg: "Building Interface...",
50433            width:450,
50434            progress:true,
50435            closable:false,
50436            modal: false
50437           
50438         });
50439         var total = mods.length;
50440         
50441         var _this = this;
50442         var progressRun = function() {
50443             if (!mods.length) {
50444                 Roo.debug && Roo.log('hide?');
50445                 Roo.MessageBox.hide();
50446                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50447                 return;    
50448             }
50449             
50450             var m = mods.shift();
50451             Roo.debug && Roo.log(m);
50452             if (typeof(m) == 'function') { // not sure if this is supported any more..
50453                 m.call(this);
50454                 return progressRun.defer(10, _this);
50455             } 
50456             
50457             Roo.MessageBox.updateProgress(
50458                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50459                     " of " + total + 
50460                     (m.name ? (' - ' + m.name) : '')
50461                     );
50462             
50463          
50464             
50465             var disabled = (typeof(m.disabled) == 'function') ?
50466                 m.disabled.call(m.module.disabled) : m.disabled;    
50467             
50468             
50469             if (disabled) {
50470                 return progressRun(); // we do not update the display!
50471             }
50472             
50473             if (!m.parent) {
50474                 // it's a top level one..
50475                 var layoutbase = new Ext.BorderLayout(document.body, {
50476                
50477                     center: {
50478                          titlebar: false,
50479                          autoScroll:false,
50480                          closeOnTab: true,
50481                          tabPosition: 'top',
50482                          //resizeTabs: true,
50483                          alwaysShowTabs: true,
50484                          minTabWidth: 140
50485                     }
50486                 });
50487                 var tree = m.tree();
50488                 tree.region = 'center';
50489                 m.el = layoutbase.addxtype(tree);
50490                 m.panel = m.el;
50491                 m.layout = m.panel.layout;    
50492                 return progressRun.defer(10, _this);
50493             }
50494             
50495             var tree = m.tree();
50496             tree.region = tree.region || m.region;
50497             m.el = m.parent.el.addxtype(tree);
50498             m.fireEvent('built', m);
50499             m.panel = m.el;
50500             m.layout = m.panel.layout;    
50501             progressRun.defer(10, _this); 
50502             
50503         }
50504         progressRun.defer(1, _this);
50505      
50506         
50507         
50508     }
50509      
50510    
50511     
50512     
50513 });
50514  //<script type="text/javascript">
50515
50516
50517 /**
50518  * @class Roo.Login
50519  * @extends Roo.LayoutDialog
50520  * A generic Login Dialog..... - only one needed in theory!?!?
50521  *
50522  * Fires XComponent builder on success...
50523  * 
50524  * Sends 
50525  *    username,password, lang = for login actions.
50526  *    check = 1 for periodic checking that sesion is valid.
50527  *    passwordRequest = email request password
50528  *    logout = 1 = to logout
50529  * 
50530  * Affects: (this id="????" elements)
50531  *   loading  (removed) (used to indicate application is loading)
50532  *   loading-mask (hides) (used to hide application when it's building loading)
50533  *   
50534  * 
50535  * Usage: 
50536  *    
50537  * 
50538  * Myapp.login = Roo.Login({
50539      url: xxxx,
50540    
50541      realm : 'Myapp', 
50542      
50543      
50544      method : 'POST',
50545      
50546      
50547      * 
50548  })
50549  * 
50550  * 
50551  * 
50552  **/
50553  
50554 Roo.Login = function(cfg)
50555 {
50556     this.addEvents({
50557         'refreshed' : true
50558     });
50559     
50560     Roo.apply(this,cfg);
50561     
50562     Roo.onReady(function() {
50563         this.onLoad();
50564     }, this);
50565     // call parent..
50566     
50567    
50568     Roo.Login.superclass.constructor.call(this, this);
50569     //this.addxtype(this.items[0]);
50570     
50571     
50572 }
50573
50574
50575 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50576     
50577     /**
50578      * @cfg {String} method
50579      * Method used to query for login details.
50580      */
50581     
50582     method : 'POST',
50583     /**
50584      * @cfg {String} url
50585      * URL to query login data. - eg. baseURL + '/Login.php'
50586      */
50587     url : '',
50588     
50589     /**
50590      * @property user
50591      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50592      * @type {Object} 
50593      */
50594     user : false,
50595     /**
50596      * @property checkFails
50597      * Number of times we have attempted to get authentication check, and failed.
50598      * @type {Number} 
50599      */
50600     checkFails : 0,
50601       /**
50602      * @property intervalID
50603      * The window interval that does the constant login checking.
50604      * @type {Number} 
50605      */
50606     intervalID : 0,
50607     
50608     
50609     onLoad : function() // called on page load...
50610     {
50611         // load 
50612          
50613         if (Roo.get('loading')) { // clear any loading indicator..
50614             Roo.get('loading').remove();
50615         }
50616         
50617         //this.switchLang('en'); // set the language to english..
50618        
50619         this.check({
50620             success:  function(response, opts)  {  // check successfull...
50621             
50622                 var res = this.processResponse(response);
50623                 this.checkFails =0;
50624                 if (!res.success) { // error!
50625                     this.checkFails = 5;
50626                     //console.log('call failure');
50627                     return this.failure(response,opts);
50628                 }
50629                 
50630                 if (!res.data.id) { // id=0 == login failure.
50631                     return this.show();
50632                 }
50633                 
50634                               
50635                         //console.log(success);
50636                 this.fillAuth(res.data);   
50637                 this.checkFails =0;
50638                 Roo.XComponent.build();
50639             },
50640             failure : this.show
50641         });
50642         
50643     }, 
50644     
50645     
50646     check: function(cfg) // called every so often to refresh cookie etc..
50647     {
50648         if (cfg.again) { // could be undefined..
50649             this.checkFails++;
50650         } else {
50651             this.checkFails = 0;
50652         }
50653         var _this = this;
50654         if (this.sending) {
50655             if ( this.checkFails > 4) {
50656                 Roo.MessageBox.alert("Error",  
50657                     "Error getting authentication status. - try reloading, or wait a while", function() {
50658                         _this.sending = false;
50659                     }); 
50660                 return;
50661             }
50662             cfg.again = true;
50663             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50664             return;
50665         }
50666         this.sending = true;
50667         
50668         Roo.Ajax.request({  
50669             url: this.url,
50670             params: {
50671                 getAuthUser: true
50672             },  
50673             method: this.method,
50674             success:  cfg.success || this.success,
50675             failure : cfg.failure || this.failure,
50676             scope : this,
50677             callCfg : cfg
50678               
50679         });  
50680     }, 
50681     
50682     
50683     logout: function()
50684     {
50685         window.onbeforeunload = function() { }; // false does not work for IE..
50686         this.user = false;
50687         var _this = this;
50688         
50689         Roo.Ajax.request({  
50690             url: this.url,
50691             params: {
50692                 logout: 1
50693             },  
50694             method: 'GET',
50695             failure : function() {
50696                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50697                     document.location = document.location.toString() + '?ts=' + Math.random();
50698                 });
50699                 
50700             },
50701             success : function() {
50702                 _this.user = false;
50703                 this.checkFails =0;
50704                 // fixme..
50705                 document.location = document.location.toString() + '?ts=' + Math.random();
50706             }
50707               
50708               
50709         }); 
50710     },
50711     
50712     processResponse : function (response)
50713     {
50714         var res = '';
50715         try {
50716             res = Roo.decode(response.responseText);
50717             // oops...
50718             if (typeof(res) != 'object') {
50719                 res = { success : false, errorMsg : res, errors : true };
50720             }
50721             if (typeof(res.success) == 'undefined') {
50722                 res.success = false;
50723             }
50724             
50725         } catch(e) {
50726             res = { success : false,  errorMsg : response.responseText, errors : true };
50727         }
50728         return res;
50729     },
50730     
50731     success : function(response, opts)  // check successfull...
50732     {  
50733         this.sending = false;
50734         var res = this.processResponse(response);
50735         if (!res.success) {
50736             return this.failure(response, opts);
50737         }
50738         if (!res.data || !res.data.id) {
50739             return this.failure(response,opts);
50740         }
50741         //console.log(res);
50742         this.fillAuth(res.data);
50743         
50744         this.checkFails =0;
50745         
50746     },
50747     
50748     
50749     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50750     {
50751         this.authUser = -1;
50752         this.sending = false;
50753         var res = this.processResponse(response);
50754         //console.log(res);
50755         if ( this.checkFails > 2) {
50756         
50757             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50758                 "Error getting authentication status. - try reloading"); 
50759             return;
50760         }
50761         opts.callCfg.again = true;
50762         this.check.defer(1000, this, [ opts.callCfg ]);
50763         return;  
50764     },
50765     
50766     
50767     
50768     fillAuth: function(au) {
50769         this.startAuthCheck();
50770         this.authUserId = au.id;
50771         this.authUser = au;
50772         this.lastChecked = new Date();
50773         this.fireEvent('refreshed', au);
50774         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50775         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50776         au.lang = au.lang || 'en';
50777         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50778         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50779         this.switchLang(au.lang );
50780         
50781      
50782         // open system... - -on setyp..
50783         if (this.authUserId  < 0) {
50784             Roo.MessageBox.alert("Warning", 
50785                 "This is an open system - please set up a admin user with a password.");  
50786         }
50787          
50788         //Pman.onload(); // which should do nothing if it's a re-auth result...
50789         
50790              
50791     },
50792     
50793     startAuthCheck : function() // starter for timeout checking..
50794     {
50795         if (this.intervalID) { // timer already in place...
50796             return false;
50797         }
50798         var _this = this;
50799         this.intervalID =  window.setInterval(function() {
50800               _this.check(false);
50801             }, 120000); // every 120 secs = 2mins..
50802         
50803         
50804     },
50805          
50806     
50807     switchLang : function (lang) 
50808     {
50809         _T = typeof(_T) == 'undefined' ? false : _T;
50810           if (!_T || !lang.length) {
50811             return;
50812         }
50813         
50814         if (!_T && lang != 'en') {
50815             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50816             return;
50817         }
50818         
50819         if (typeof(_T.en) == 'undefined') {
50820             _T.en = {};
50821             Roo.apply(_T.en, _T);
50822         }
50823         
50824         if (typeof(_T[lang]) == 'undefined') {
50825             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50826             return;
50827         }
50828         
50829         
50830         Roo.apply(_T, _T[lang]);
50831         // just need to set the text values for everything...
50832         var _this = this;
50833         /* this will not work ...
50834         if (this.form) { 
50835             
50836                
50837             function formLabel(name, val) {
50838                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50839             }
50840             
50841             formLabel('password', "Password"+':');
50842             formLabel('username', "Email Address"+':');
50843             formLabel('lang', "Language"+':');
50844             this.dialog.setTitle("Login");
50845             this.dialog.buttons[0].setText("Forgot Password");
50846             this.dialog.buttons[1].setText("Login");
50847         }
50848         */
50849         
50850         
50851     },
50852     
50853     
50854     title: "Login",
50855     modal: true,
50856     width:  350,
50857     //height: 230,
50858     height: 180,
50859     shadow: true,
50860     minWidth:200,
50861     minHeight:180,
50862     //proxyDrag: true,
50863     closable: false,
50864     draggable: false,
50865     collapsible: false,
50866     resizable: false,
50867     center: {  // needed??
50868         autoScroll:false,
50869         titlebar: false,
50870        // tabPosition: 'top',
50871         hideTabs: true,
50872         closeOnTab: true,
50873         alwaysShowTabs: false
50874     } ,
50875     listeners : {
50876         
50877         show  : function(dlg)
50878         {
50879             //console.log(this);
50880             this.form = this.layout.getRegion('center').activePanel.form;
50881             this.form.dialog = dlg;
50882             this.buttons[0].form = this.form;
50883             this.buttons[0].dialog = dlg
50884             this.buttons[1].form = this.form;
50885             this.buttons[1].dialog = dlg;
50886            
50887            //this.resizeToLogo.defer(1000,this);
50888             // this is all related to resizing for logos..
50889             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50890            //// if (!sz) {
50891              //   this.resizeToLogo.defer(1000,this);
50892              //   return;
50893            // }
50894             //var w = Ext.lib.Dom.getViewWidth() - 100;
50895             //var h = Ext.lib.Dom.getViewHeight() - 100;
50896             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50897             //this.center();
50898             if (this.disabled) {
50899                 this.hide();
50900                 return;
50901             }
50902             
50903             if (this.user.id < 0) { // used for inital setup situations.
50904                 return;
50905             }
50906             
50907             if (this.intervalID) {
50908                 // remove the timer
50909                 window.clearInterval(this.intervalID);
50910                 this.intervalID = false;
50911             }
50912             
50913             
50914             if (Roo.get('loading')) {
50915                 Roo.get('loading').remove();
50916             }
50917             if (Roo.get('loading-mask')) {
50918                 Roo.get('loading-mask').hide();
50919             }
50920             
50921             //incomming._node = tnode;
50922             this.form.reset();
50923             //this.dialog.modal = !modal;
50924             //this.dialog.show();
50925             this.el.unmask(); 
50926             
50927             
50928             this.form.setValues({
50929                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50930                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50931             });
50932             
50933             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50934             if (this.form.findField('username').getValue().length > 0 ){
50935                 this.form.findField('password').focus();
50936             } else {
50937                this.form.findField('username').focus();
50938             }
50939     
50940         }
50941     },
50942     items : [
50943          {
50944        
50945             xtype : 'ContentPanel',
50946             xns : Roo,
50947             region: 'center',
50948             fitToFrame : true,
50949             
50950             items : [
50951     
50952                 {
50953                
50954                     xtype : 'Form',
50955                     xns : Roo.form,
50956                     labelWidth: 100,
50957                     style : 'margin: 10px;',
50958                     
50959                     listeners : {
50960                         actionfailed : function(f, act) {
50961                             // form can return { errors: .... }
50962                                 
50963                             //act.result.errors // invalid form element list...
50964                             //act.result.errorMsg// invalid form element list...
50965                             
50966                             this.dialog.el.unmask();
50967                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50968                                         "Login failed - communication error - try again.");
50969                                       
50970                         },
50971                         actioncomplete: function(re, act) {
50972                              
50973                             Roo.state.Manager.set(
50974                                 this.dialog.realm + '.username',  
50975                                     this.findField('username').getValue()
50976                             );
50977                             Roo.state.Manager.set(
50978                                 this.dialog.realm + '.lang',  
50979                                 this.findField('lang').getValue() 
50980                             );
50981                             
50982                             this.dialog.fillAuth(act.result.data);
50983                               
50984                             this.dialog.hide();
50985                             
50986                             if (Roo.get('loading-mask')) {
50987                                 Roo.get('loading-mask').show();
50988                             }
50989                             Roo.XComponent.build();
50990                             
50991                              
50992                             
50993                         }
50994                     },
50995                     items : [
50996                         {
50997                             xtype : 'TextField',
50998                             xns : Roo.form,
50999                             fieldLabel: "Email Address",
51000                             name: 'username',
51001                             width:200,
51002                             autoCreate : {tag: "input", type: "text", size: "20"}
51003                         },
51004                         {
51005                             xtype : 'TextField',
51006                             xns : Roo.form,
51007                             fieldLabel: "Password",
51008                             inputType: 'password',
51009                             name: 'password',
51010                             width:200,
51011                             autoCreate : {tag: "input", type: "text", size: "20"},
51012                             listeners : {
51013                                 specialkey : function(e,ev) {
51014                                     if (ev.keyCode == 13) {
51015                                         this.form.dialog.el.mask("Logging in");
51016                                         this.form.doAction('submit', {
51017                                             url: this.form.dialog.url,
51018                                             method: this.form.dialog.method
51019                                         });
51020                                     }
51021                                 }
51022                             }  
51023                         },
51024                         {
51025                             xtype : 'ComboBox',
51026                             xns : Roo.form,
51027                             fieldLabel: "Language",
51028                             name : 'langdisp',
51029                             store: {
51030                                 xtype : 'SimpleStore',
51031                                 fields: ['lang', 'ldisp'],
51032                                 data : [
51033                                     [ 'en', 'English' ],
51034                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51035                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51036                                 ]
51037                             },
51038                             
51039                             valueField : 'lang',
51040                             hiddenName:  'lang',
51041                             width: 200,
51042                             displayField:'ldisp',
51043                             typeAhead: false,
51044                             editable: false,
51045                             mode: 'local',
51046                             triggerAction: 'all',
51047                             emptyText:'Select a Language...',
51048                             selectOnFocus:true,
51049                             listeners : {
51050                                 select :  function(cb, rec, ix) {
51051                                     this.form.switchLang(rec.data.lang);
51052                                 }
51053                             }
51054                         
51055                         }
51056                     ]
51057                 }
51058                   
51059                 
51060             ]
51061         }
51062     ],
51063     buttons : [
51064         {
51065             xtype : 'Button',
51066             xns : 'Roo',
51067             text : "Forgot Password",
51068             listeners : {
51069                 click : function() {
51070                     //console.log(this);
51071                     var n = this.form.findField('username').getValue();
51072                     if (!n.length) {
51073                         Roo.MessageBox.alert("Error", "Fill in your email address");
51074                         return;
51075                     }
51076                     Roo.Ajax.request({
51077                         url: this.dialog.url,
51078                         params: {
51079                             passwordRequest: n
51080                         },
51081                         method: this.dialog.method,
51082                         success:  function(response, opts)  {  // check successfull...
51083                         
51084                             var res = this.dialog.processResponse(response);
51085                             if (!res.success) { // error!
51086                                Roo.MessageBox.alert("Error" ,
51087                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51088                                return;
51089                             }
51090                             Roo.MessageBox.alert("Notice" ,
51091                                 "Please check you email for the Password Reset message");
51092                         },
51093                         failure : function() {
51094                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51095                         }
51096                         
51097                     });
51098                 }
51099             }
51100         },
51101         {
51102             xtype : 'Button',
51103             xns : 'Roo',
51104             text : "Login",
51105             listeners : {
51106                 
51107                 click : function () {
51108                         
51109                     this.dialog.el.mask("Logging in");
51110                     this.form.doAction('submit', {
51111                             url: this.dialog.url,
51112                             method: this.dialog.method
51113                     });
51114                 }
51115             }
51116         }
51117     ]
51118   
51119   
51120 })
51121  
51122
51123
51124